Reputation: 193
I'm having an issue on my development machine which seems isolated to this machine and I can't figure it out. I have a jQuery file uploader which posts the user-selected file to a PHP script for handling using XmlHttpRequest. The script works fine on my MacBook Pro running OSX10.6.3 with MAMP 1.9, however on my iMac with the exact same operating system and version of MAMP, with an identical server image, it fails.
I have traced the cause of the error down to the property $_SERVER["CONTENT_LENGTH"]
returning0, even though I can get the filename just fine and everything else seems to have succeeded about the request. For some reason it just won't seem to give me the actual content length. Here is the code that is causing the problem - the function in question is
getSize()
.
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"]; //*THIS* is returning 0
} else {
throw new Exception('Getting content length is not supported.');
}
}
}
Upvotes: 9
Views: 9342
Reputation: 5622
It's possible that chunked encoding is being used, which wouldn't send content length.
Upvotes: 3
Reputation: 2083
$headers = apache_request_headers();
echo $headers['Content-Length'];
I'll assume this one also returns 0?
Upvotes: 0
Reputation: 3776
I am using the same plugin, the latest version seems to be working fine even with older browsers. I am still having some displaying/rendering issues with IE6 and IE7, but I solved those by making the button opaque and adding an image to cover it. I also modified the receiving PHP script to be just one function instead than multiple function. Not suitable for all the occasions, but it works fine for me for all the browsers:
public function web_upload_file_ajax(){
$return = array();
$uploaded_file = array('name'=>'', 'size'=>0);
// list of valid extensions, ex. array("jpeg", "xml", "bmp")
$allowedExtensions = array('jpg', 'jpeg', 'png', 'gif', 'bmp','txt','csv');
// max file size in bytes
$sizeLimit = 3 * 1024 * 1024;
//folder to upload the file to - add slash at end
$uploadDirectory = TMPPATH.'tmp_upload'.DIRECTORY_SEPARATOR;
if(!is_dir($uploadDirectory)){
@mkdir($uploadDirectory, 0766, true);
}
if(!is_dir($uploadDirectory)){
$return = array('error' => 'Server error. Impossible to create the cache folder:'.$uploadDirectory);
}elseif(!is_writable($uploadDirectory)){
$return = array('error' => 'Server error. Upload directory is not writable.');
} else {
$postSize = $this->bytes_to_num(ini_get('post_max_size'));
$uploadSize = $this->bytes_to_num(ini_get('upload_max_filesize'));
if ($postSize < $sizeLimit || $uploadSize < $sizeLimit){
$size = max(1, $sizeLimit / 1024 / 1024) . 'M';
$return = array('error' => 'increase post_max_size and upload_max_filesize to '.$size);
}elseif (isset($_GET['qqfile'])) {
$uploaded_file['name'] = $_GET['qqfile'];
if (isset($_SERVER['CONTENT_LENGTH'])){
$uploaded_file['size'] = (int)$_SERVER['CONTENT_LENGTH'];
} else {
$return = array('error'=>'Getting content length is not supported.');
}
} elseif (isset($_FILES['qqfile'])) {
$uploaded_file['name'] = $_FILES['qqfile']['name'];
$uploaded_file['size'] = $_FILES['qqfile']['size'];
} else {
$return = array('error' => 'No files were uploaded.');
}
if(count($return)==0){
if($uploaded_file['size'] == 0) $return = array('error' => 'File is empty');
elseif($uploaded_file['size'] > $sizeLimit) $return = array('error' => 'File is too large');
elseif($uploaded_file['name']!=''){
$pathinfo = pathinfo($uploaded_file['name']);
$filename = $pathinfo['filename'];
$ext = $pathinfo['extension'];
if($allowedExtensions && !in_array(strtolower($ext), $allowedExtensions)){
$return = array('error' => 'File has an invalid extension, it should be one of '.implode(', ', $allowedExtensions).'.');
}
}
}
if(count($return)==0){
// overwrite previous files that were uploaded
$filename = ll('sessions')->get_id();
// don't overwrite previous files that were uploaded
while (file_exists($uploadDirectory.$filename.'.'.$ext)) {
$filename .= rand(10, 99);
}
$saved = false;
$path = $uploadDirectory.$filename.'.'.$ext;
if (isset($_GET['qqfile'])) {
$input = fopen('php://input', 'r');
$temp = tmpfile();
$realSize = stream_copy_to_stream($input, $temp);
fclose($input);
if ($realSize != $uploaded_file['size']){
$saved = false;
} else {
$target = fopen($path, 'w');
fseek($temp, 0, SEEK_SET);
stream_copy_to_stream($temp, $target);
fclose($target);
$saved = true;
}
} else {
if(!move_uploaded_file($_FILES['qqfile']['tmp_name'], $path)){
$saved = false;
}
$saved = true;
}
if ($saved){
$return = array('success'=>true, 'file'=>$filename.'.'.$ext);
} else {
$return = array('error'=> 'Could not save uploaded file. The upload was cancelled, or server error encountered');
}
}
}
// to pass data through iframe you will need to encode all html tags
echo htmlspecialchars(json_encode($return), ENT_NOQUOTES);
}
and here is the bytes_to_num that I use as an helper function, but you can include this into the same function as well if you want:
/**
* This function transforms bytes (like in the the php.ini notation) for numbers (like '2M') to an integer (2*1024*1024 in this case)
*/
public function bytes_to_num($bytes){
$bytes = trim($bytes);
$ret = $bytes+0;
if($ret==0 || strlen($ret)>=strlen($bytes)){
return $ret;
}
$type = substr($bytes, strlen($ret));
switch(strtoupper($type)){
case 'P':
case 'Pb':
$ret *= 1024;
case 'T':
case 'Tb':
$ret *= 1024;
case 'G':
case 'Gb':
$ret *= 1024;
case 'M':
case 'Mb':
$ret *= 1024;
case 'K':
case 'Kb':
$ret *= 1024;
break;
}
return $ret;
}
Upvotes: 0
Reputation: 193
Solved it! Seems the jQuery script I am using fails under firefox 3.5.x, I updated to 3.6.9 and it works fine.
Now I have to find some way to make it backwards compatible with older versions of firefox.
Upvotes: 7
Reputation: 53536
Did you set your encoding type to multipart/form-data
?
<form action="upload.php" method="post" enctype="multipart/form-data">
...
<input type="file" ... />
...
</form>
Upvotes: 3