I've been doing research on image file upload security for a while but can't seem to crack it. I want to add some security to the fantastic Valums Ajax Uploader by checking whether or not a file is actually an image before saving to a server. The only extensions allowed are .jpg, .png and .gif, which of course the extensions are checked but I'm looking to validate it's an image file via GD processing. I know very little about this subject so I'm taking cues from this and this post. As it stands now, I can easily save a random file with an image extension and it will be saved the server, which I want to prevent. Here is the script I've come up with thus far, which I've added to the handleUpload function in the php.php file. Unfortunately the result is that it returns an error no matter what file I upload, valid image or not. Please excuse my utter newbishness.
$newIm = #imagecreatefromjpeg($uploadDirectory . $filename . '.' . $ext);
$newIm2 = #imagecreatefrompng($uploadDirectory . $filename . '.' . $ext);
$newIm3 = #imagecreatefromgif($uploadDirectory . $filename . '.' . $ext);
if (!$newIm && !$newIm2 && !$newIm3) {
return array('error' => 'File is not an image. Please try again');
}else{
imagedestroy($newIm);
imagedestroy($newim2);
imagedestroy($newIm3);
}
Here's most of my php.php file. By the way, my files are not being submitted via a regular form but by the default uploader:
class qqUploadedFileXhr {
/**
* Save the file to the specified path
* #return boolean TRUE on success
*/
function save($path) {
$input = fopen("php://input", "r");
$temp = tmpfile();
$realSize = stream_copy_to_stream($input, $temp);
fclose($input);
if ($realSize != $this->getSize()){
return false;
}
$target = fopen($path, "w");
fseek($temp, 0, SEEK_SET);
stream_copy_to_stream($temp, $target);
fclose($target);
return true;
}
function getName() {
return $_GET['qqfile'];
}
function getSize() {
if (isset($_SERVER["CONTENT_LENGTH"])){
return (int)$_SERVER["CONTENT_LENGTH"];
} else {
throw new Exception('Getting content length is not supported.');
}
}
}
/**
* Handle file uploads via regular form post (uses the $_FILES array)
*/
class qqUploadedFileForm {
/**
* Save the file to the specified path
* #return boolean TRUE on success
*/
function save($path) {
if(!move_uploaded_file($_FILES['qqfile']['tmp_name'], $path)){
return false;
}
return true;
}
function getName() {
return $_FILES['qqfile']['name'];
}
function getSize() {
return $_FILES['qqfile']['size'];
}
}
class qqFileUploader {
private $allowedExtensions = array();
private $sizeLimit = 2097152;
private $file;
function __construct(array $allowedExtensions = array(), $sizeLimit = 2097152){
$allowedExtensions = array_map("strtolower", $allowedExtensions);
$this->allowedExtensions = $allowedExtensions;
$this->sizeLimit = $sizeLimit;
$this->checkServerSettings();
if (isset($_GET['qqfile'])) {
$this->file = new qqUploadedFileXhr();
} elseif (isset($_FILES['qqfile'])) {
$this->file = new qqUploadedFileForm();
} else {
$this->file = false;
}
}
private function checkServerSettings(){
$postSize = $this->toBytes(ini_get('post_max_size'));
$uploadSize = $this->toBytes(ini_get('upload_max_filesize'));
if ($postSize < $this->sizeLimit || $uploadSize < $this->sizeLimit){
$size = max(1, $this->sizeLimit / 1024 / 1024) . 'M';
die("{'error':'increase post_max_size and upload_max_filesize to $size'}");
}
}
private function toBytes($str){
$val = trim($str);
$last = strtolower($str[strlen($str)-1]);
switch($last) {
case 'g': $val *= (1024 * 1024 * 1024);
case 'm': $val *= (1024 * 1024);
case 'k': $val *= 1024;
}
return $val;
}
/**
* Returns array('success'=>true) or array('error'=>'error message')
*/
function handleUpload($uploadDirectory, $replaceOldFile = FALSE){
if (!is_writable($uploadDirectory)){
return array('error' => "Server error. Upload directory isn't writable.");
}
if (!$this->file){
return array('error' => 'No files were uploaded.');
}
$size = $this->file->getSize();
if ($size == 0) {
return array('error' => 'File is empty');
}
if ($size > $this->sizeLimit) {
return array('error' => 'File is too large, please upload files that are less than 2MB');
}
$pathinfo = pathinfo($this->file->getName());
$filename = $pathinfo['filename'];
//$filename = md5(uniqid());
$ext = $pathinfo['extension'];
if($this->allowedExtensions && !in_array(strtolower($ext), $this->allowedExtensions)){
$these = implode(', ', $this->allowedExtensions);
return array('error' => 'File has an invalid extension, it should be one of '. $these . '.');
}
if(!$replaceOldFile){
/// don't overwrite previous files that were uploaded
while (file_exists($uploadDirectory . $filename . '.' . $ext)) {
$filename .= rand(10, 99);
}
}
$newIm = #imagecreatefromjpeg($uploadDirectory . $filename . '.' . $ext);
$newIm2 = #imagecreatefrompng($uploadDirectory . $filename . '.' . $ext);
$newIm3 = #imagecreatefromgif($uploadDirectory . $filename . '.' . $ext);
if (!$newIm && !$newIm2 && !$newIm3) {
return array('error' => 'File is not an image. Please try again');
}else{
imagedestroy($newIm);
imagedestroy($newim2);
imagedestroy($newIm3);
}
if ($this->file->save($uploadDirectory . $filename . '.' . $ext)){
// At this point you could use $result to do resizing of images or similar operations
if(strtolower($ext) == 'jpg' || strtolower($ext) == 'jpeg' || strtolower($ext) == 'gif' || strtolower($ext) == 'png'){
$imgSize=getimagesize($uploadDirectory . $filename . '.' . $ext);
if($imgSize[0] > 100 || $imgSize[1]> 100){
$thumbcheck = make_thumb($uploadDirectory . $filename . '.' . $ext,$uploadDirectory . "thumbs/" . $filename ."_thmb" . '.' . $ext,100,100);
if($thumbcheck == "true"){
$thumbnailPath = $uploadDirectory . "thumbs/" . $filename ."_thmb". '.' . $ext;
}
}else{
$this->file->save($uploadDirectory . "thumbs/" . $filename ."_thmb" . '.' . $ext);
$thumbnailPath = $uploadDirectory . "thumbs/" . $filename ."_thmb". '.' . $ext;
}
if($imgSize[0] > 500 || $imgSize[1] > 500){
resize_orig($uploadDirectory . $filename . '.' . $ext,$uploadDirectory . $filename . '.' . $ext,500,500);
$imgPath = $uploadDirectory . $filename . '.' . $ext;
$newsize = getimagesize($imgPath);
$imgWidth = ($newsize[0]+30);
$imgHeight = ($newsize[1]+50);
}else{
$imgPath = $uploadDirectory . $filename . '.' . $ext;
$newsize = getimagesize($imgPath);
$imgWidth = ($newsize[0]+30);
$imgHeight = ($newsize[1]+50);
}
}
return array('success'=>true,
'thumbnailPath'=>$thumbnailPath,
'imgPath'=>$imgPath,
'imgWidth'=>$imgWidth,
'imgHeight'=>$imgHeight
);
} else {
return array('error'=> 'Could not save uploaded file.' .
'The upload was cancelled, or server error encountered');
}
}
}
// list of valid extensions, ex. array("jpeg", "xml", "bmp")
$allowedExtensions = array();
// max file size in bytes
$sizeLimit = 2097152;
$uploader = new qqFileUploader($allowedExtensions, $sizeLimit);
$result = $uploader->handleUpload('uploads/');
// to pass data through iframe you will need to encode all html tags
echo htmlspecialchars(json_encode($result), ENT_NOQUOTES);
If someone can point me in the right direction about what I'm doing wrong, it would be most appreciated.
Edit:
Thanks Mark B for the help and clarification. So the new code (which I pasted in the same location as the old, the handleUpload function) is still throwing an error no matter what I upload, so I wonder if it needs to be in a different place -- I'm just not sure where to put it.
$newIm = getimagesize($uploadDirectory . $filename . '.' . $ext);
if ($newIm === FALSE) {
return array('error' => 'File is not an image. Please try again');
}
Second Edit:
I believe the answer is here, still trying to implement it.
Just use getimagesize():
$newIm = getimagesize$uploadDirectory . $filename . '.' . $ext');
if ($newIm === FALSE) {
die("Hey! what are you trying to pull?");
}
The problem with your code is the 3 imagecreate calls and subsequent if() statement. It should have been written as an OR clause. "if any of the image load attempts fail, then complain". Yours is written as "if all image attempts fail", which is not possible if a gif/jpg/png actually was uploaded.
The answer detailed here works like a charm, I wish I had seen it before posting. I hope I implemented it correctly...as I have it, it works!
class qqUploadedFileXhr {
/**
* Save the file to the specified path
* #return boolean TRUE on success
*/
function save($path) {
// Store the file in tmp dir, to validate it before storing it in destination dir
$input = fopen('php://input', 'r');
$tmpPath = tempnam(sys_get_temp_dir(), 'upload'); // upl is 3-letter prefix for upload
$tmpStream = fopen($tmpPath, 'w'); // For writing it to tmp dir
$realSize = stream_copy_to_stream($input, $tmpStream);
fclose($input);
fclose($tmpStream);
if ($realSize != $this->getSize()){
return false;
}
$newIm = getimagesize($tmpPath);
if ($newIm === FALSE) {
return false;
}else{
// Store the file in destination dir, after validation
$pathToFile = $path . $filename;
$destination = fopen($pathToFile, 'w');
$tmpStream = fopen($tmpPath, 'r'); // For reading it from tmp dir
stream_copy_to_stream($tmpStream, $destination);
fclose($destination);
fclose($tmpStream);
return true;
}
}
function getName() {
return $_GET['qqfile'];
}
function getSize() {
if (isset($_SERVER["CONTENT_LENGTH"])){
return (int)$_SERVER["CONTENT_LENGTH"];
} else {
throw new Exception('Getting content length is not supported.');
}
}
}
Related
I am developing a module for my client to upload and browse file in Opencart.
when I am uploading file from my back-end server I am getting the output as file.zip.xyzasdf. Where I just want to remove this .xyzasdf
Can any one suggest me how to remove sanitize from the following code...
public function upload() {
$this->load->language('catalog/download');
$json = array();
// Check user has permission
if (!$this->user->hasPermission('modify', 'catalog/download')) {
$json['error'] = $this->language->get('error_permission');
}
if (!$json) {
if (!empty($this->request->files['file']['name']) && is_file($this->request->files['file']['tmp_name'])) {
// Sanitize the filename
$filename = basename(html_entity_decode($this->request->files['file']['name'], ENT_QUOTES, 'UTF-8'));
// Validate the filename length
if ((utf8_strlen($filename) < 3) || (utf8_strlen($filename) > 128)) {
$json['error'] = $this->language->get('error_filename');
}
// Allowed file extension types
$allowed = array();
$extension_allowed = preg_replace('~\r?\n~', "\n", $this->config->get('config_file_ext_allowed'));
$filetypes = explode("\n", $extension_allowed);
foreach ($filetypes as $filetype) {
$allowed[] = trim($filetype);
}
if (!in_array(strtolower(substr(strrchr($filename, '.'), 1)), $allowed)) {
$json['error'] = $this->language->get('error_filetype');
}
// Allowed file mime types
$allowed = array();
$mime_allowed = preg_replace('~\r?\n~', "\n", $this->config->get('config_file_mime_allowed'));
$filetypes = explode("\n", $mime_allowed);
foreach ($filetypes as $filetype) {
$allowed[] = trim($filetype);
}
if (!in_array($this->request->files['file']['type'], $allowed)) {
$json['error'] = $this->language->get('error_filetype');
}
// Check to see if any PHP files are trying to be uploaded
$content = file_get_contents($this->request->files['file']['tmp_name']);
if (preg_match('/\<\?php/i', $content)) {
$json['error'] = $this->language->get('error_filetype');
}
// Return any upload error
if ($this->request->files['file']['error'] != UPLOAD_ERR_OK) {
$json['error'] = $this->language->get('error_upload_' . $this->request->files['file']['error']);
}
} else {
$json['error'] = $this->language->get('error_upload');
}
}
if (!$json) {
$file = $filename . '.' . token(32);
move_uploaded_file($this->request->files['file']['tmp_name'], DIR_FOLDER . $file);
$json['filename'] = $file;
$json['mask'] = $filename;
$json['success'] = $this->language->get('text_upload');
}
$this->response->addHeader('Content-Type: application/json');
$this->response->setOutput(json_encode($json));
}
Any help would be greatly appreciated...
Thanks
Removing the random string that is added to the filename is simple. Just change
move_uploaded_file($this->request->files['file']['tmp_name'], DIR_UPLOAD . $file);
to:
move_uploaded_file($this->request->files['file']['tmp_name'], DIR_UPLOAD . $filename);
But keep in mind that this will bring problems.
OpenCart saves the random string in the database at the time of file upload, so it will later use it to identify the file.
If you delete this feature, the uploaded files in the admin panel will not be available.
I am using PHP (Symfony2) in my project which has image upload feature. Inside controller:
if ($request->isXmlHttpRequest() && $request->isMethod('POST')) {
$index=(int)$request->request->get('index');
$image_file = $request->files->get('shop_bundle_managementbundle_posttype')['images'][$index]['file'];
$image= new Image();
$image->setFile($image_file);
$image->setSubDir('hg');
$image->upload();
$em->persist($image);
$em->flush();
}
I use a class UploadFileMover that handle the file upload. I didn't write the following code but as I understand, an MD5 hash will be created from the original file name and used as filename. But the instance of UploadedFile contains a file name like "PHP"+number.tmp, not the original as stored in computer filesystem.
class UploadFileMover {
public function moveUploadedFile(UploadedFile $file, $uploadBasePath,$relativePath)
{
$originalName = $file->getFilename();
$targetFileName = $relativePath . DIRECTORY_SEPARATOR . $originalName;
$targetFilePath = $uploadBasePath . DIRECTORY_SEPARATOR . $targetFileName;
$ext = $file->getExtension();
$i=1;
while (file_exists($targetFilePath) && md5_file($file->getPath()) != md5_file($targetFilePath)) {
if ($ext) {
$prev = $i == 1 ? "" : $i;
$targetFilePath = $targetFilePath . str_replace($prev . $ext, $i++ . $ext, $targetFilePath);
} else {
$targetFilePath = $targetFilePath . $i++;
}
}
$targetDir = $uploadBasePath . DIRECTORY_SEPARATOR . $relativePath;
if (!is_dir($targetDir)) {
$ret = mkdir($targetDir, umask(), true);
if (!$ret) {
throw new \RuntimeException("Could not create target directory to move temporary file into.");
}
}
$file->move($targetDir, basename($targetFilePath));
return str_replace($uploadBasePath . DIRECTORY_SEPARATOR, "", $targetFilePath);
}
}
This class is instanciated when an image is uploaded. In other words, I have an Entity Image that has a method upload. Inside entity class:
public function upload()
{
if (null === $this->getFile()) {
return;
}
$uploadFileMover = new UploadFileMover();
$this->path = $uploadFileMover->moveUploadedFile($this->file, self::getUploadDir(),$this->subDir);
$this->file = null;
}
I var_dumped the filename all across the different steps but I cannot figure out where it is transformed to PHP16653.tmp.
Can it be related to an APACHE related configuration? Your help is appreciated. I really did a lot of research for similar issue in the web to no avail.
The problem was created by the line:
$originalName = $file->getFilename();
Use:
$originalName = $file->getClientOriginalName();
instead.
I got ascript which helps me to add some data into a csv file, based on the fact if a image is inside a folder or not (exits or not). Files are images, so I need to check if the file exists, and if it is a png, jpg, jpeg, gif.
So far it only check if it a JPG but I would like it to find the file exists if it's a PNG or JPEG or even GIF.
<?php
$columns = array("row1","row2","row3","row4","row5","row6","row7","row8","row9",
"row10","row11","row12","row13","row14","row15","row16","row17","row18"
);
$rootDir = "/path/to/images/folder/files";
$file = fopen("database.csv", "r") or die('fopen database failed');
$newFile = fopen("newdata.csv", "w") or die('fopen newdata.csv failed');
while (($data = fgetcsv($file, 999999, ";")) !== FALSE) {
$row = array_combine($columns, $data);
$filename = $row['row4'].".jpg"; // could be png or jpEg, or even gif
if (file_exists("$rootDir/$filename")) {
$row['image'] = .$filename; //also needs correct extension of image which exists.
$row['small_image'] = .$filename;
$row['thumbnail'] = .$filename;
}
fputcsv($newFile, array_values($row), ";",'"' );
}
fclose($file);
fclose($newFile);
?>
You can do something like this:
// your code
$possible_extensions = array("jpg", "jpeg", "png", "gif");
$row = array_combine($columns, $data);
foreach($possible_extensions as $ext){
$filename = $row['row4'] . "." . $ext;
if (file_exists("$rootDir/$filename")) {
$row['image'] = .$filename;
$row['small_image'] = .$filename;
$row['thumbnail'] = .$filename;
break;
}
}
fputcsv($newFile, array_values($row), ";",'"' );
// your code
Edited:
If you want to perform case-insensitive file_exists() check then here's the solution,
The following fileExists() function returns the full path file if found, and false if not.
function fileExists($fileName, $caseSensitive = true) {
if(file_exists($fileName)) {
return $fileName;
}
if($caseSensitive) return false;
// Handle case insensitive requests
$directoryName = dirname($fileName);
$fileArray = glob($directoryName . '/*', GLOB_NOSORT);
$fileNameLowerCase = strtolower($fileName);
foreach($fileArray as $file) {
if(strtolower($file) == $fileNameLowerCase) {
return $file;
}
}
return false;
}
Here's the source:
PHP Case Insensitive Version of file_exists()
And now your code,
// your code
$possible_extensions = array("jpg", "jpeg", "png", "gif");
$row = array_combine($columns, $data);
foreach($possible_extensions as $ext){
$filename = $row['row4'] . "." . $ext;
if ($filename = fileExists("$rootDir/$filename", false)) {
$row['image'] = .$filename; //also needs correct extension of image which exists.
$row['small_image'] = .$filename;
$row['thumbnail'] = .$filename;
break;
}
}
fputcsv($newFile, array_values($row), ";",'"' );
// your code
I have my HTML file:
<html>
<head>
<meta charset="utf-8">
<title>Fine Uploader Demo</title>
<link href="fineuploader-3.4.1.css" rel="stylesheet">
</head>
<body>
<div id="fine-uploader"></div>
<script src="jquery-1.7.2.min.js"></script>
<script src="jquery.fineuploader-3.4.1.js"></script>
<script>
function createUploader() {
var uploader = new qq.FineUploader({
// Pass the HTML element here
element: document.getElementById('fine-uploader'),
// or, if using jQuery
// element: $('#fine-uploader')[0],
// Use the relevant server script url here
// if it's different from the default “/server/upload”
request: {
endpoint: 'qqFileUploader'
}
});
}
window.onload = createUploader;
</script>
</body>
</html>
My PHP Code qqFileUploader.php
<?php
class qqFileUploader {
public $allowedExtensions = array();
public $sizeLimit = null;
public $inputName = 'qqfile';
public $chunksFolder = 'chunks';
public $chunksCleanupProbability = 0.001; // Once in 1000 requests on avg
public $chunksExpireIn = 604800; // One week
protected $uploadName;
function __construct(){
$this->sizeLimit = $this->toBytes(ini_get('upload_max_filesize'));
}
/**
* Get the original filename
*/
public function getName(){
if (isset($_REQUEST['qqfilename']))
return $_REQUEST['qqfilename'];
if (isset($_FILES[$this->inputName]))
return $_FILES[$this->inputName]['name'];
}
/**
* Get the name of the uploaded file
*/
public function getUploadName(){
return $this->uploadName;
}
/**
* Process the upload.
* #param string $uploadDirectory Target directory.
* #param string $name Overwrites the name of the file.
*/
public function handleUpload($uploadDirectory, $name = null){
if (is_writable($this->chunksFolder) &&
1 == mt_rand(1, 1/$this->chunksCleanupProbability)){
// Run garbage collection
$this->cleanupChunks();
}
// Check that the max upload size specified in class configuration does not
// exceed size allowed by server config
if ($this->toBytes(ini_get('post_max_size')) < $this->sizeLimit ||
$this->toBytes(ini_get('upload_max_filesize')) < $this->sizeLimit){
$size = max(1, $this->sizeLimit / 1024 / 1024) . 'M';
return array('error'=>"Server error. Increase post_max_size and upload_max_filesize to ".$size);
}
// is_writable() is not reliable on Windows (http://www.php.net/manual/en/function.is-executable.php#111146)
// The following tests if the current OS is Windows and if so, merely checks if the folder is writable;
// otherwise, it checks additionally for executable status (like before).
$isWin = (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN');
$folderInaccessible = ($isWin) ? !is_writable($uploadDirectory) : ( !is_writable($uploadDirectory) && !is_executable($uploadDirectory) );
if ($folderInaccessible){
return array('error' => "Server error. Uploads directory isn't writable" . (!$isWin) ? " or executable." : ".");
}
if(!isset($_SERVER['CONTENT_TYPE'])) {
return array('error' => "No files were uploaded.");
} else if (strpos(strtolower($_SERVER['CONTENT_TYPE']), 'multipart/') !== 0){
return array('error' => "Server error. Not a multipart request. Please set forceMultipart to default value (true).");
}
// Get size and name
$file = $_FILES[$this->inputName];
$size = $file['size'];
if ($name === null){
$name = $this->getName();
}
// Validate name
if ($name === null || $name === ''){
return array('error' => 'File name empty.');
}
// Validate file size
if ($size == 0){
return array('error' => 'File is empty.');
}
if ($size > $this->sizeLimit){
return array('error' => 'File is too large.');
}
// Validate file extension
$pathinfo = pathinfo($name);
$ext = isset($pathinfo['extension']) ? $pathinfo['extension'] : '';
if($this->allowedExtensions && !in_array(strtolower($ext), array_map("strtolower", $this->allowedExtensions))){
$these = implode(', ', $this->allowedExtensions);
return array('error' => 'File has an invalid extension, it should be one of '. $these . '.');
}
// Save a chunk
$totalParts = isset($_REQUEST['qqtotalparts']) ? (int)$_REQUEST['qqtotalparts'] : 1;
if ($totalParts > 1){
$chunksFolder = $this->chunksFolder;
$partIndex = (int)$_REQUEST['qqpartindex'];
$uuid = $_REQUEST['qquuid'];
if (!is_writable($chunksFolder) && !is_executable($uploadDirectory)){
return array('error' => "Server error. Chunks directory isn't writable or executable.");
}
$targetFolder = $this->chunksFolder.DIRECTORY_SEPARATOR.$uuid;
if (!file_exists($targetFolder)){
mkdir($targetFolder);
}
$target = $targetFolder.'/'.$partIndex;
$success = move_uploaded_file($_FILES[$this->inputName]['tmp_name'], $target);
// Last chunk saved successfully
if ($success AND ($totalParts-1 == $partIndex)){
$target = $this->getUniqueTargetPath($uploadDirectory, $name);
$this->uploadName = basename($target);
$target = fopen($target, 'wb');
for ($i=0; $i<$totalParts; $i++){
$chunk = fopen($targetFolder.DIRECTORY_SEPARATOR.$i, "rb");
stream_copy_to_stream($chunk, $target);
fclose($chunk);
}
// Success
fclose($target);
for ($i=0; $i<$totalParts; $i++){
unlink($targetFolder.DIRECTORY_SEPARATOR.$i);
}
rmdir($targetFolder);
return array("success" => true);
}
return array("success" => true);
} else {
$target = $this->getUniqueTargetPath($uploadDirectory, $name);
if ($target){
$this->uploadName = basename($target);
if (move_uploaded_file($file['tmp_name'], $target)){
return array('success'=> true);
}
}
return array('error'=> 'Could not save uploaded file.' .
'The upload was cancelled, or server error encountered');
}
}
/**
* Returns a path to use with this upload. Check that the name does not exist,
* and appends a suffix otherwise.
* #param string $uploadDirectory Target directory
* #param string $filename The name of the file to use.
*/
protected function getUniqueTargetPath($uploadDirectory, $filename)
{
// Allow only one process at the time to get a unique file name, otherwise
// if multiple people would upload a file with the same name at the same time
// only the latest would be saved.
if (function_exists('sem_acquire')){
$lock = sem_get(ftok(__FILE__, 'u'));
sem_acquire($lock);
}
$pathinfo = pathinfo($filename);
$base = $pathinfo['filename'];
$ext = isset($pathinfo['extension']) ? $pathinfo['extension'] : '';
$ext = $ext == '' ? $ext : '.' . $ext;
$unique = $base;
$suffix = 0;
// Get unique file name for the file, by appending random suffix.
while (file_exists($uploadDirectory . DIRECTORY_SEPARATOR . $unique . $ext)){
$suffix += rand(1, 999);
$unique = $base.'-'.$suffix;
}
$result = $uploadDirectory . DIRECTORY_SEPARATOR . $unique . $ext;
// Create an empty target file
if (!touch($result)){
// Failed
$result = false;
}
if (function_exists('sem_acquire')){
sem_release($lock);
}
return $result;
}
/**
* Deletes all file parts in the chunks folder for files uploaded
* more than chunksExpireIn seconds ago
*/
protected function cleanupChunks(){
foreach (scandir($this->chunksFolder) as $item){
if ($item == "." || $item == "..")
continue;
$path = $this->chunksFolder.DIRECTORY_SEPARATOR.$item;
if (!is_dir($path))
continue;
if (time() - filemtime($path) > $this->chunksExpireIn){
$this->removeDir($path);
}
}
}
/**
* Removes a directory and all files contained inside
* #param string $dir
*/
protected function removeDir($dir){
foreach (scandir($dir) as $item){
if ($item == "." || $item == "..")
continue;
unlink($dir.DIRECTORY_SEPARATOR.$item);
}
rmdir($dir);
}
/**
* Converts a given size with units to bytes.
* #param string $str
*/
protected function toBytes($str){
$val = trim($str);
$last = strtolower($str[strlen($str)-1]);
switch($last) {
case 'g': $val *= 1024;
case 'm': $val *= 1024;
case 'k': $val *= 1024;
}
return $val;
}
}
When I upload as file, I get the follwing two errors and my file upload fails:
Error 1
OPTIONS file:///C:/Users/upload/qqFileUploader jquery.fineuploader-3.4.1.js:3903
handleStandardFileUpload jquery.fineuploader-3.4.1.js:3903
api.upload jquery.fineuploader-3.4.1.js:3989
upload jquery.fineuploader-3.4.1.js:3041
qq.FineUploaderBasic._upload jquery.fineuploader-3.4.1.js:1437
qq.FineUploaderBasic._uploadFileOrBlobDataList jquery.fineuploader-3.4.1.js:1415
qq.FineUploaderBasic.addFiles jquery.fineuploader-3.4.1.js:1049
qq.FineUploaderBasic._onInputChange jquery.fineuploader-3.4.1.js:1340
qq.UploadButton.onChange jquery.fineuploader-3.4.1.js:1117
(anonymous function) jquery.fineuploader-3.4.1.js:680
Error 2
[FineUploader] Error when attempting to parse xhr response text (SyntaxError: Unexpected end of input) jquery.fineuploader-3.4.1.js:155
qq.log jquery.fineuploader-3.4.1.js:155
qq.FineUploaderBasic.log jquery.fineuploader-3.4.1.js:939
qq.UploadHandler.log jquery.fineuploader-3.4.1.js:1146
parseResponse jquery.fineuploader-3.4.1.js:3683
onComplete jquery.fineuploader-3.4.1.js:3732
(anonymous function) jquery.fineuploader-3.4.1.js:3766
When debug is set to true, I get this:
[FineUploader] xhr - server response received for 0 jquery.fineuploader-3.4.1.js:150
[FineUploader] responseText = <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL php/qqFileUploader.php was not found on this server.</p>
<hr>
<address>Apache/2.2.15 (CentOS) Server at Port 80</address>
</body></html>
You haven't set your endpoint correctly, it appears. Your endpoint must be a relative or absolute URL pointing at the location on your server setup to handle Fine Uploader's requests. It also looks like you may not be running your app in a webserver. It seems like you are simply opening up a web browser and pointing at an html file on your filesystem. That is never going to work.
Please see the demos on fineuploader.com as well as the request option documentation in the readme.
I searched for solution to this the better part of the day and I'm still in the dark. For a client of mine I created a simple web gallery for uploading images and I'm using valums file uploader. Till now I didn't have any problems with it on any other site I created, except miss-configuration which I solved some time ago.
So, what is the problem?
When I upload a image with filesize below 260KB it works fine. But, when I'm uploading a bigger image, it doesn't throw me back any error. I just get an empty thumbnail, because image wasn't uploaded.
When I open Chrome Console, this is what I see:
POST http://designflowstudio.com/gallery2/include/upload.php?url=uploads%2Fe558c4dfc2a0f0d60f5ebff474c97ffc&fid=7&qqfile=316267269718409738080100.jpg 403 (Forbidden) fileuploader.js:1463
POST http://designflowstudio.com/gallery2/include/upload.php?url=uploads%2Fe558c4dfc2a0f0d60f5ebff474c97ffc&fid=7&qqfile=3.jpg 403 (Forbidden) fileuploader.js:1463
On some occasions I got 413 error "Request entity too large".
Let me say that this problem really annoys me because the script works well on my local web server (apache) and on my web server. Here I can upload as may and as big images as I can find, but on the client's web server...
If anyone have time and will to help me, here is the JavaScript that I use to call the valums uploader:
function createUploader(){
var uploader = new qq.FileUploaderBasic({
debug: true,
multiple: true,
allowedExtensions: [<?php $tmp=".";foreach($allowedExt as $ext){$tmp.="'$ext', ";}echo substr($tmp,1,strlen($tmp)-3);?>],
button: document.getElementById('uploadDiv'),
action: '<?php echo$home;?>include/upload.php',
sizeLimit: <?php echo$sizeLimit;?>,
forceMultipart: true,
params: {'url':'uploads/<?php echo$g['path'];?>','fid':'<?php echo$g['id'];?>'},
onSubmit: function(id, fName){$('#upload-list').append('<div id="upload-list-'+id+'" class="gallery" rel="'+fName+'"><div class="progress'+id+'"></div>');$('.progress'+id).progressbar({value:0})},
onProgress: function(id, fName, loaded, total){
var p = 0;
p = parseFloat(loaded/total*100);
if(isNaN(p)) p = '';
$('.progress'+id).progressbar("option","value",p);
},
onComplete: function(id,fName,json){
if(json.error){
$('#upload-list-'+id).html(fName+'<br />'+json.error);
}else{
$('#upload-list-'+id)
.attr("rel",json.fname)
.html('<img class="cmd" id="deleteImg" rel="'+json.id+'" src="<?php echo$home;?>images/delete.png" title="Delete picture" /><img class="img" src="<?php echo$home;?>uploads/<?php echo$g['path'];?>/th_'+json.fname+'" /><input class="ut" type="text" name="name" value="'+json.name+'" /><input class="ud" type="text" name="desc" /><div class="cl"></div>')
.attr("id",json.id);
}
},
onError: function(id,fName,error){
console.log(id+' '+fName+' '+error);
}
});
}
window.onload = createUploader;
And here is my PHP for server processing the file:
require("config.php");
require("SimpleImage.php");
ini_set("log_errors" , "1");
ini_set("error_log" , "php-errors-upload.log");
ini_set("display_errors" , "1");
/**
* Handle file uploads via XMLHttpRequest
*/
class qqUploadedFileXhr {
/**
* Save the file to the specified path
* #return boolean TRUE on success
*/
function save($path) {
$input = fopen("php://input", "r");
$temp = tmpfile();
$realSize = stream_copy_to_stream($input, $temp);
fclose($input);
if ($realSize != $this->getSize()){
return false;
}
$target = fopen($path, "w");
fseek($temp, 0, SEEK_SET);
stream_copy_to_stream($temp, $target);
fclose($target);
return true;
}
function getName() {
return $_GET['qqfile'];
}
function getSize() {
if (isset($_SERVER["CONTENT_LENGTH"])){
return (int)$_SERVER["CONTENT_LENGTH"];
} else {
throw new Exception('Getting content length is not supported.');
}
}
}
/**
* Handle file uploads via regular form post (uses the $_FILES array)
*/
class qqUploadedFileForm {
/**
* Save the file to the specified path
* #return boolean TRUE on success
*/
function save($path) {
if(!move_uploaded_file($_FILES['qqfile']['tmp_name'], $path)){
return false;
}
return true;
}
function getName() {
return $_FILES['qqfile']['name'];
}
function getSize() {
return $_FILES['qqfile']['size'];
}
}
class qqFileUploader {
private $allowedExtensions = array();
private $sizeLimit = 20485760;
private $file;
function __construct(array $allowedExtensions = array(), $sizeLimit = 20485760){
$allowedExtensions = array_map("strtolower", $allowedExtensions);
$this->allowedExtensions = $allowedExtensions;
$this->sizeLimit = $sizeLimit;
$this->checkServerSettings();
if (isset($_GET['qqfile'])) {
$this->file = new qqUploadedFileXhr();
} elseif (isset($_FILES['qqfile'])) {
$this->file = new qqUploadedFileForm();
} else {
$this->file = false;
}
}
private function checkServerSettings(){
$postSize = $this->toBytes(ini_get('post_max_size'));
$uploadSize = $this->toBytes(ini_get('upload_max_filesize'));
if ($postSize < $this->sizeLimit || $uploadSize < $this->sizeLimit){
$size = max(1, $this->sizeLimit / 1024 / 1024) . 'M';
die("{'error':'increase post_max_size and upload_max_filesize to $size'}");
}
}
private function toBytes($str){
$val = trim($str);
$last = strtolower($str[strlen($str)-1]);
switch($last) {
case 'g': $val *= 1024;
case 'm': $val *= 1024;
case 'k': $val *= 1024;
}
return $val;
}
/**
* Returns array('success'=>true) or array('error'=>'error message')
*/
function handleUpload($uploadDirectory, $replaceOldFile = FALSE){
chdir("../");
if(!file_exists($uploadDirectory)){
foreach(explode("/",$uploadDirectory) as $val){
if(empty($val2)){$val2=$val."/";}else{$val2.=$val."/";}
if($val<>""){if(!file_exists($val2)){mkdir($val2);}}
}
}
if (!is_writable($uploadDirectory)){
return array('error' => "Server error. Upload directory isn't writable.");
}
if (!$this->file){
return array('error' => 'No files were uploaded.');
}
$size = $this->file->getSize();
if ($size == 0) {
return array('error' => 'File is empty');
}
if ($size > $this->sizeLimit) {
return array('error' => 'File is too large');
}
$pathinfo = pathinfo($this->file->getName());
$filename = md5($pathinfo['filename'].mt_rand());
//$filename = md5(uniqid());
$ext = strtolower($pathinfo['extension']);
if($this->allowedExtensions && !in_array(strtolower($ext), $this->allowedExtensions)){
$these = implode(', ', $this->allowedExtensions);
return array('error' => 'File has an invalid extension, it should be one of '. $these . '.');
}
if(!$replaceOldFile){
/// don't overwrite previous files that were uploaded
while (file_exists($uploadDirectory . $filename . '.' . $ext)) {
$filename .= rand(10, 99);
}
}
if ($this->file->save($uploadDirectory .'/'. $filename . '.' . $ext)){
global $sql_images;
$md=md5($filename);
$tmp=mysql_query("SELECT `order` FROM `$sql_images` WHERE `gid`='{$_REQUEST['fid']}' ORDER BY `order` DESC");
if($tmp&&mysql_num_rows($tmp)>0){$i=mysql_result($tmp,0);$i++;}else{$i=0;}
mysql_query("INSERT INTO `$sql_images` (`order`,`gid`,`name`,`path`) VALUES ('$i','{$_REQUEST['fid']}','{$pathinfo['filename']}','$filename.$ext')");
$image = new SimpleImage();
$image->load($uploadDirectory.'/'.$filename.'.'.$ext);
$image->resizeToWidth(150);
$image->save($uploadDirectory.'/th_'.$filename.'.'.$ext);
return array('success'=>true,'fname'=>$filename.".".$ext,'name'=>$pathinfo['filename'],'id'=>mysql_insert_id());
} else {
return array('error'=> 'Could not save uploaded file.' .
'The upload was cancelled, or server error encountered');
}
}
}
// list of valid extensions, ex. array("jpeg", "xml", "bmp")
$allowedExtensions = array();
// max file size in bytes
$sizeLimit = 20*1024*1024;
$uploader = new qqFileUploader($allowedExtensions, $sizeLimit);
$result = $uploader->handleUpload($_REQUEST['url']);
// to pass data through iframe you will need to encode all html tags
echo htmlspecialchars(json_encode($result), ENT_NOQUOTES);
Let me say again, all the calls to MySQL, files with require() are valid. The scripts work on two other servers, but on this one, not.
Thank you for your time and will to help.
You have some configuration issues on your server if it is choking due to the content-type header. Instead of commenting out this line, to work around this messed up server, you should perhaps try to set the forceMultipart option to true, assuming your server can handle multipart-encoded requests correctly. This all assumes you are using the 2.1-SNAPSHOT version of the uploader, as the option I mentioned first appeared in this version.