search for in the  
<Dealing with XFormsError Messages Explained>
Last updated: Thu, 19 May 2005

Chapter 38. Handling file uploads

POST method uploads

This feature lets people upload both text and binary files. With PHP's authentication and file manipulation functions, you have full control over who is allowed to upload and what is to be done with the file once it has been uploaded.

PHP is capable of receiving file uploads from any RFC-1867 compliant browser (which includes Netscape Navigator 3 or later, Microsoft Internet Explorer 3 with a patch from Microsoft, or later without a patch).

Related Configurations Note: See also the file_uploads, upload_max_filesize, upload_tmp_dir, post_max_size and max_input_time directives in php.ini

PHP also supports PUT-method file uploads as used by Netscape Composer and W3C's Amaya clients. See the PUT Method Support for more details.

Example 38-1. File Upload Form

A file upload screen can be built by creating a special form which looks something like this:

<!-- The data encoding type, enctype, MUST be specified as below -->
<form enctype="multipart/form-data" action="__URL__" method="POST">
    <!-- MAX_FILE_SIZE must precede the file input field -->
    <input type="hidden" name="MAX_FILE_SIZE" value="30000" />
    <!-- Name of input element determines name in $_FILES array -->
    Send this file: <input name="userfile" type="file" />
    <input type="submit" value="Send File" />
</form>

The __URL__ in the above example should be replaced, and point to a PHP file.

The MAX_FILE_SIZE hidden field (measured in bytes) must precede the file input field, and its value is the maximum filesize accepted. This is an advisory to the browser, PHP also checks it. Fooling this setting on the browser side is quite easy, so never rely on files with a greater size being blocked by this feature. The PHP settings for maximum-size, however, cannot be fooled. This form element should always be used as it saves users the trouble of waiting for a big file being transferred only to find that it was too big and the transfer failed.

Note: Be sure your file upload form has attribute enctype="multipart/form-data" otherwise the file upload will not work.

The global $_FILES exists as of PHP 4.1.0 (Use $HTTP_POST_FILES instead if using an earlier version). These arrays will contain all the uploaded file information.

The contents of $_FILES from the example form is as follows. Note that this assumes the use of the file upload name userfile, as used in the example script above. This can be any name.

$_FILES['userfile']['name']

The original name of the file on the client machine.

$_FILES['userfile']['type']

The mime type of the file, if the browser provided this information. An example would be "image/gif".

$_FILES['userfile']['size']

The size, in bytes, of the uploaded file.

$_FILES['userfile']['tmp_name']

The temporary filename of the file in which the uploaded file was stored on the server.

$_FILES['userfile']['error']

The error code associated with this file upload. This element was added in PHP 4.2.0

Files will, by default be stored in the server's default temporary directory, unless another location has been given with the upload_tmp_dir directive in php.ini. The server's default directory can be changed by setting the environment variable TMPDIR in the environment in which PHP runs. Setting it using putenv() from within a PHP script will not work. This environment variable can also be used to make sure that other operations are working on uploaded files, as well.

Example 38-2. Validating file uploads

See also the function entries for is_uploaded_file() and move_uploaded_file() for further information. The following example will process the file upload that came from a form.

<?php
// In PHP versions earlier than 4.1.0, $HTTP_POST_FILES should be used instead
// of $_FILES.

$uploaddir = '/var/www/uploads/';
$uploadfile = $uploaddir . basename($_FILES['userfile']['name']);

echo
'<pre>';
if (
move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {
   echo
"File is valid, and was successfully uploaded.\n";
} else {
   echo
"Possible file upload attack!\n";
}

echo
'Here is some more debugging info:';
print_r($_FILES);

print
"</pre>";

?>

The PHP script which receives the uploaded file should implement whatever logic is necessary for determining what should be done with the uploaded file. You can, for example, use the $_FILES['userfile']['size'] variable to throw away any files that are either too small or too big. You could use the $_FILES['userfile']['type'] variable to throw away any files that didn't match a certain type criteria. As of PHP 4.2.0, you could use $_FILES['userfile']['error'] and plan your logic according to the error codes. Whatever the logic, you should either delete the file from the temporary directory or move it elsewhere.

If no file is selected for upload in your form, PHP will return $_FILES['userfile']['size'] as 0, and $_FILES['userfile']['tmp_name'] as none.

The file will be deleted from the temporary directory at the end of the request if it has not been moved away or renamed.

Example 38-3. Uploading array of files

PHP supports HTML array feature even with files.

<form action="" method="post" enctype="multipart/form-data">
<p>Pictures:
<input type="file" name="pictures[]" />
<input type="file" name="pictures[]" />
<input type="file" name="pictures[]" />
<input type="submit" value="Send" />
</p>
</form>
<?php
foreach ($_FILES["pictures"]["error"] as $key => $error) {
   if (
$error == UPLOAD_ERR_OK) {
      
$tmp_name = $_FILES["pictures"]["tmp_name"][$key];
      
$name = $_FILES["pictures"]["name"][$key];
      
move_uploaded_file($tmp_name, "data/$name");
   }
}
?>


User Contributed Notes
Handling file uploads
Andrea_1
30-Apr-2005 11:47
I want to share a really easy way of uploading files to your web host from a form!

Someone suggested it way down in these notes. I tried it and it's working!!

Go to http://pear.php.net/index.php

Then go to:
http://pear.php.net/HTTP_Upload

You download the main pear.php files and then the HTTP_Upload file. Upload it to your web host and within minutes, the example will be working!

I'm going to look further on that pear.php.net site too - it looks like they have a lot of good addition for complex PHP functions!

Yahoo!
Steven
29-Apr-2005 06:30
Need to get around your PHP.ini file upload limit?
Use a bit of clever JavaScript to get the value of your <input type="file"> (the location of the file on the client's machine), copy it to a hidden text box then try and upload traditionally, if you get a PHP error UPLOAD_ERR_INI_SIZE, then use the value of your hidden text box to initiate a PHP-FTP connection and upload to your heart's content. 
No more limits :)
Sa_Estahbanati at yahoo dot com
27-Apr-2005 07:47
In the above notes there was solution to the server-side code that would use move_uploaded_file() to save the temporary file into a permanent path.
A pal maitaned that relative path would have problem in windows servers and you should indicate the complete path instead. While it is almost impossible to ge to know the exact path when you are not the host, I tried to test other ways and finally found out that a simple "." (dot) before tha path would solve the relative pathing problem.
instead of '/var/www/uploads/' or 'E:/sitebase/var/www/uploads/' you should type './var/www/uploads/'
Here is the complete source:
       <?php
          
// In PHP versions earlier than 4.1.0, $HTTP_POST_FILES should be used instead
           // of $_FILES.

           //$uploaddir = 'E:/sitebase/var/www/uploads/';
           //$uploaddir = '/var/www/uploads/';
          
$uploaddir = './var/www/uploads/';
          
$uploadfile = $uploaddir . basename($_FILES['userfile']['name']);

           echo
'<pre>';
           if (
move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {
               echo
"File is valid, and was successfully uploaded.\n";
           } else {
               echo
"Possible file upload attack!\n";
           }

           echo
'Here is some more debugging info:';
          
print_r($_FILES);

           print
"</pre>";

      
?>
dmsuperman at comcast dot net
26-Apr-2005 01:00
I needed a file uploader for a client a little while ago, then the client didn't want it, so I'll share with all of you. I know I hated coding it, it was confusing (for me anyway), but I made it fairly simple to use:

<?
function uploader($num_of_uploads=1, $file_types_array=array("txt"), $max_file_size=1048576, $upload_dir=""){
  if(!
is_numeric($max_file_size)){
  
$max_file_size = 1048576;
  }
  if(!isset(
$_POST["submitted"])){
  
$form = "<form action='".$PHP_SELF."' method='post' enctype='multipart/form-data'>Upload files:<br /><input type='hidden' name='submitted' value='TRUE' id='".time()."'><input type='hidden' name='MAX_FILE_SIZE' value='".$max_file_size."'>";
   for(
$x=0;$x<$num_of_uploads;$x++){
    
$form .= "<input type='file' name='file[]'><font color='red'>*</font><br />";
   }
  
$form .= "<input type='submit' value='Upload'><br /><font color='red'>*</font>Maximum file length (minus extension) is 15 characters. Anything over that will be cut to only 15 characters. Valid file type(s): ";
   for(
$x=0;$x<count($file_types_array);$x++){
     if(
$x<count($file_types_array)-1){
      
$form .= $file_types_array[$x].", ";
     }else{
      
$form .= $file_types_array[$x].".";
     }
   }
  
$form .= "</form>";
   echo(
$form);
  }else{
   foreach(
$_FILES["file"]["error"] as $key => $value){
     if(
$_FILES["file"]["name"][$key]!=""){
       if(
$value==UPLOAD_ERR_OK){
        
$origfilename = $_FILES["file"]["name"][$key];
        
$filename = explode(".", $_FILES["file"]["name"][$key]);
        
$filenameext = $filename[count($filename)-1];
         unset(
$filename[count($filename)-1]);
        
$filename = implode(".", $filename);
        
$filename = substr($filename, 0, 15).".".$filenameext;
        
$file_ext_allow = FALSE;
         for(
$x=0;$x<count($file_types_array);$x++){
           if(
$filenameext==$file_types_array[$x]){
            
$file_ext_allow = TRUE;
           }
         }
         if(
$file_ext_allow){
           if(
$_FILES["file"]["size"][$key]<$max_file_size){
             if(
move_uploaded_file($_FILES["file"]["tmp_name"][$key], $upload_dir.$filename)){
               echo(
"File uploaded successfully. - <a href='".$upload_dir.$filename."' target='_blank'>".$filename."</a><br />");
             }else{
               echo(
$origfilename." was not successfully uploaded<br />");
             }
           }else{
             echo(
$origfilename." was too big, not uploaded<br />");
           }
         }else{
           echo(
$origfilename." had an invalid file extension, not uploaded<br />");
         }
       }else{
         echo(
$origfilename." was not successfully uploaded<br />");
       }
     }
   }
  }
}
?>

uploader([int num_uploads [, arr file_types [, int file_size [, str upload_dir ]]]]);

num_uploads = Number of uploads to handle at once.

file_types = An array of all the file types you wish to use. The default is txt only.

file_size = The maximum file size of EACH file. A non-number will results in using the default 1mb filesize.

upload_dir = The directory to upload to, make sure this ends with a /

This functions echo()'s the whole uploader, and submits to itself, you need not do a thing but put uploader(); to have a simple 1 file upload with all defaults.
oportocala at DON'T GET SPAM dot yahoo dot comerce
22-Apr-2005 08:59
I had some problems with uploading files because of the path .So to save you some problems. if you get an error like "failed to open stream: No such file or directory in ..." it's likely because your not specifing the full path of the directory you want to upload.In other words you need to specify the full path and not the relative path in the second parameter.
Here is a small function to upload stuff. The files get their new name via last index from a database.
<?php
 
  $name_tmp
= $HTTP_POST_FILES['file']['tmp_name'];
//read temporay filename
 
$name = $HTTP_POST_FILES['file']['name'];
//read initial filename
 
if(is_uploaded_file($name_tmp)){
  
$ext =explode('.', $name);
  
$ext = $ext[count($ext)-1];
  
//get extension of uploaded file
      
$index=getLastIndex();//costum function
  
$path_parts = pathinfo(__file__);//get path info of curent php script
  
$dir = $path_parts['dirname']."\\uploads"; //full path of upload directory
  
$new = $dir."\\".$index.".".$ext;
     if(
move_uploaded_file($name_tmp,$new))
       echo
"File transfer succesfull.";
   }

?>
Jared
20-Apr-2005 09:07
If you want a unique filenames to prevent overwritting is to use sha1_file().

<?php
if ($_SERVER['REQUEST_METHOD'] == 'POST')
   {
       if (isset(
$_FILES['pic']))
       {
          
$file = $_FILES['pic'];

           if (
$file['error'] == UPLOAD_ERR_OK)
           {
              
$name = sha1_file($file['tmp_name']);
              
$pathInfo = pathinfo($file['name']);
               if (isset(
$pathInfo['extension']))
                  
$name .= '.'.$pathInfo['extension'];

               if (
move_uploaded_file($file['tmp_name'], $uploaddir.$name))
               {
                  
// ..ok..
              
}
               else
               {
                  
// ..failed..
              
}
           }
       }
   }
?>

Its unlikely 2 files will ever get the same filename, unless they are the same.
trog at swmud dot pl
12-Apr-2005 06:07
The best way of getting unique filenames, in order to
prevent accidental file overwriting during e.g. upload is
to use functions:
- uniqid
- time
- md5

It is not recommended to use just time() function.
The example code will explain.
The code:
<?php
  
for( $i = 0; $i < 10; $i++ )
       echo
'Time: '.time().' ID: '.md5(uniqid(time())).".\n";
?>

Example output:
Time: 1113310579 ID: 79955ae9bc455a33aed92de6b83e1a4a.
Time: 1113310579 ID: 45de8a2173b53c33659cafa1d564f49a.
Time: 1113310579 ID: 9377a0e655efd206ca5583b6530706cd.
Time: 1113310579 ID: 06a86b2c9678adb7f222984019a14c10.
Time: 1113310579 ID: 25fe1664af35ebb3347f04cf703be968.
Time: 1113310579 ID: dca7397eb4d7d5c44e34b0a16a6fc89e.
Time: 1113310579 ID: fa01b6a2a83fbaefd89306d27835101f.
Time: 1113310579 ID: cf20ada42f2248c3c8b67f46b1d97401.
Time: 1113310579 ID: 9ab1a24c7f576fe0e6add43b848660ac.
Time: 1113310579 ID: c6aa6e9e314b4392c94432ece411824e.

The ids are unique and this is what we want.
Now just append or prepend this id to the original filename and we
have unique filename every time.

Example:

<?php
   $unique_id
= md5(uniqid(time()));
  
$filename = $unique_id.'_'.$filename;
?>
bryan dot fillmer at beasleyallen dot com
04-Apr-2005 10:08
I looked high and low for a simple way to rename files as they are uploaded to prevent users from overwriting each others files. Below is the script I came up with.

<?php

  
foreach( $_FILES["screenshots"]["error"] as $key => $error ) {
  
       if(
$error == UPLOAD_ERR_OK ) {
          
$tmp_name = $_FILES["screenshots"]["tmp_name"][$key];
          
$original_name = $_FILES["screenshots"]["name"][$key];
          
$tmp_array = preg_split( "/\\\/",$tmp_name );
          
$file_name = $tmp_array[count($array)-1];
          
$extension = preg_replace( "/(.*)\.([a-z]+)$/","$2",$original_name );
          
$new_name = preg_replace( "/(.*)\.([a-z]+)$/","$1.$extension",$file_name );
          
move_uploaded_file( $tmp_name,"./screenshots/$new_name" );
       }
  
   }

?>

I started with the generic multiple file upload script in the documentation above and worked from there.
robpet at tds dot net
03-Apr-2005 12:35
People have remarked that incorrect permissions on the upload directory may prevent photos or other files from uploading.  Setting the Apache owner of the directory incorrectly will also prevent files from uploading -- I use a PHP script that creates a directory (if it doesn't exist already) before placing an uploaded file into it.  When the script creates the directory and then copies the uploaded file into the directory there is no problem because the owner of the file is whatever Apache is running as, typically "nobody". However, lets say that I've moved the site to a new server and have copied over existing file directories using FTP.  In this case the owner will have a different name from the Apache owner and files will not upload. The solution is to TelNet into the site and reset the owner to "nobody" or whatever Apache is running as using the CHOWN command.
javasri at yahoo dot com
30-Mar-2005 04:34
On windows XP, SP2, Explorer at times fails to upload files without extensions.

$_FILES array is null in that case. Microsoft says its a security feature(!)

The only solution we could comeup is to enforce uploaded file  to have an extention.
mike at debitage dot com
28-Mar-2005 11:24
I read all of the above and while enlightening was not able to find a solution to my problem.  I ws getting some "file not found and failed to open stream" type errors.  I finally fixed the above example code so it worked on my host by making this simple change:

$uploaddir = '';
james [at] adultphpsolutions [dot] com
24-Mar-2005 02:21
Well let me ask you this?  Why would you compromise security doing this?  Might as well just turn off IP tables, you're basically allowing any connections to any port on your server, bad move.  I don't have those and have no problem uploading files. 

"The iptables rules for this are as follows:
# allow all fragments
-A INPUT -f -j ACCEPT
# allow icmp traffic
-A INPUT -p icmp -j ACCEPT" <-- From guy below me...

On the bright side, this will also fix some issues with ssh and NFS =)

On another note, a smart programmer would also do checking on the files that are uploaded, making sure that they are the correct file, it is another breach in security not by checking the files you are uploading are in a web viewable area.  Just my two cents...
thisisroot at gmail dot com
14-Mar-2005 09:33
If you're having problems uploading large files but small files go through fine, here are some things to try:

- In the HTML form, make sure you've set MAX_FILE_SIZE to an acceptable value.
- In php.ini, be sure you've set the the upload_max_filesize and post_max_size to be large enough to handle your upload.
- In your httpd.conf (if you're using apache), check that the LimitRequestBody directive isn't set too low (it's optional, so it may not be there at all).

If those settings don't work, you can check your firewall configuration. When uploading large files, packets have to be split into fragments of varying size depending on your systems MTU (maximum transmission unit), which is typically 1500 bytes.

Because some systems send the packets with the headers last (or the header packet may be received after some of the data packets), firewalls can't filter this traffic based on destination port and address. Many firewalls (including iptables) have to be configured to allow fragments separately from standard traffic. Unfortunately, it's an all-or-nothing thing in these cases, and exploits based on packet fragmentation have been a problem in the past (teardrop, boink, etc.). Note that ICMP may be used to notify the host (your server) of oncoming fragmentation, so you may need to allow ICMP traffic as well.

The iptables rules for this are as follows:
# allow all fragments
-A INPUT -f -j ACCEPT
# allow icmp traffic
-A INPUT -p icmp -j ACCEPT

On the bright side, this will also fix some issues with ssh and NFS =)
27-Feb-2005 12:06
Just to remind everyone, if you are wanting to upload larger files, you will need to change the value of both upload_max_filesize and post_max_size to the largest filesize you would like to allow.  Then restart apache and everything should work.
infoworks-tn dot com at david dot hamilton dot nospam
27-Feb-2005 10:52
Regarding the post by therebechips below about saving attachments to a MySQL database.  Here are some items that he forgot to mention:

Make sure your datafield is type text for storing the chunks in.  base64_encode translates binary into text (in a nutshell). 

Also, the max size for a text field is 65535.  In his example he used 50,000 for the MAX_SQL chunks to put into the database.  If you do this, the encoded data is too large to fit in the field and is truncated.  Use 40,000 instead to be safe.  It took me a few hours to realize why my images were coming back out of the database corrupted.

Other than that, his example worked very well. 
If you want to email me, flip the two parts of my email address around and remove .nospam

David Hamilton
Leevi at izilla dot com dot au
09-Feb-2005 12:52
This may help a newbie to file uploads.. it took advice from a friend to fix it..

If you are using
-windows xp
-iis 5
-php 5

If you keep getting permission errors on file uploads... and you have sworn you set the permissions to write to the directory in iis...

double check that
a) in windows explorer under tools > folder options
click the view tab
scroll down all the way to "use simple file sharing (recommended)"
uncheck this box

b) find the folder you wish to upload to on your server
c) click properties and then the security tab
d) make sure the appropriate write settings are checked.

you may want to test by setting "everyone" to have full permission....

BEWARE doing this will open up big security holes on your server....

hope this helps

Leevi Graham
phpnoob at adam dot net dot nz
08-Feb-2005 07:57
Just thought I'd add this since I had to search forever to find an answer. 

When I used the enctype attribute on a form to process a file upload I had a problem redirecting back to an anchor point on the original page my code looked like this on the upload page:

header("Location: original_page.php#image_gal");

and the resulting url looked like this:

http://www.url.com/original_page.php

note the missing anchor reference.  So my work around was to pass the anchor point as a variable and then redirect again when I got to the original page.  A little bit chunky but it worked.  Hope this helps someone.
Tyfud
07-Jan-2005 10:44
It's important to note that when using the move_uploaded_file() command, that some configurations (Especially IIS) will fail if you prefix the destination path with a leading "/". Try the following:

move_uploaded_file($tmpFileName,'uploads/'.$fileName);

Setting up permissions is also a must. Make sure all accounts have write access to your upload directory, and read access if you wish to view these files later. You might have to chmod() the directory or file afterwards as well if you're still getting access errors.
lobo235 at gmail dot com
04-Jan-2005 04:48
Be sure to be careful with the $_FILES['userfile']['name'] array element. If the client uploads a file that has an apostrophe in the filename it WILL NOT get set to the full name of the file from the client's machine.

For example, if the client uploads a file named george's car.jpg the $_FILES['userfile']['name'] element will be set to s car.jpg because PHP appears to cut off everything before the apostrophe as well as the apostrophe itself.

This did not happen in some of the previous versions of PHP but I know that it happens in version 4.3.10 so watch out for this.

I thought this was a bug so I submitted it but it turns out that it is a "security measure"
ryan dot baclit at gmail dot com
15-Dec-2004 01:15
Hello everyone. I want to share to you that uploading will never work out of the box if you didn't set the upload_tmp_dir directive in your php.ini file in the first place. If you just compiled the source files as is and tried to upload, you're in for a big mess. I don't know the flags to pass to the configure script to tell php about the default temporary directory to place the uploaded files.

In case your php upload code won't do as expected, open up the php.ini file and set the upload_tmp_dir. Then restart the Apache server and you're set.

By the way, I'm using Linux Mandrake 10.1 Official and PHP 4.3.9 on Apache 2.0.49.
captlid
08-Nov-2004 12:37
mime_content_type() is better to use if you want to find if a file sent is really a jpeg or a plain text file. :)
dev at kiwicore dot org
13-Oct-2004 02:58
Becuase uploading seems to rarely work "out of the box" even if you follow the above instructions, I'll reiterate: the main reason people fail to have successful uploads, is because the directory that you are uploading to must have the right permissions, 0755 seems to work fine. I'd also like to submit my shot at an end all, be all upload capturing script.

Usage :

list($success,$response) = captureUpload('./user_images/',false,'_upload');

Will look inside $_FILES['upload'] for an uploaded file. The second parameter is the name of a callbaack function (false, if you don't want it), to rename the file, it will send two parameters:
  
     $file (array of file info) , $destinationDirectory

It should return the name of the file. This is useful if you want to do a database lookup (for CMS integration, to see who uploaded it, etc), or do some other fancy checking.

The function will also rename file "whatever_copy_1.gif" if "whatever.gif" already exists.

And here is the code:

function captureUpload($destDir,$nameCallback = false,$fieldName = '_upload',$maxFileSize = false){
  
   //make sure something is there
   if(!isset($_FILES[$fieldName]) ||!isset($_FILES)||!is_array($_FILES[$fieldName]) ||!$_FILES[$fieldName]['name'])
       return array(false,'No files were uploaded. Make sure your form tag\'s enctype was set to multipart/form-data and that the right field is being checked for the uploaded file.');
  
   //normalize the file variable
   $file = $_FILES[$fieldName];
   if (!isset($file['type']))      $file['type']      = '';
   if (!isset($file['size']))      $file['size']      = '';
   if (!isset($file['tmp_name']))  $file['tmp_name']  = '';
   $file['name'] = preg_replace(
             '/[^a-zA-Z0-9\.\$\%\'\`\-\@\{\}\~\!\#\(\)\&\_\^]/'
             ,'',str_replace(array(' ','%20'),array('_','_'),$file['name']));
  
   //was it to big?
   if($maxFileSize && ($file['size'] > $maxFileSize))
       return array(false,'The file uploaded was to large.');
  
   //normalize destDir
   if(strlen($destDir)>0 && $destDir[strlen($destDir)-1] != "/")
       $destDir = $destDir.'/';
      
   //should we change the filename via a callback?
   if($nameCallback)
       $file['name'] = call_user_func_array($nameCallback, array($file,$destDir));
  
   $i = 0;   
   //if the filename already exists, append _copy_x (with extension)
   if(strpos($file['name'],'.') !== false){
       $bits = explode('.',$file['name']);
       $ext = array_pop($bits);
       while(file_exists($destDir.implode('.', $bits).($i?'_copy_'.$i:'').'.'.$ext)){
           ++$i;
           $file['name'] = implode('.',$bits).($i?'_copy_'.$i:'').'.'.$ext;
       }
  
   //if the filename already exists, append _copy_x (no extension)
   } else {
       while(file_exists($destDir.$file['name'].($i ?'_copy_'.$i:''))){
           ++$i;
           $file['name'] = $file['name'].($i?'_copy_'.$i:'');
       }
   }
  
   //and now the big moment
   if(!@copy($file['tmp_name'], $destDir.$file['name']))
       return array(false,'Could not write the file "'.$file['name'].'" to: "'.$destDir.'". Permission denied.');
   else
       return array(true,$file['name']);
}
therhinoman at hotmail dot com
27-Aug-2004 03:20
If your upload script is meant only for uploading images, you can use the image function getimagesize() (does not require the GD image library) to make sure you're really getting an image and also filter image types.

<?php getimagesize($file); ?>

...will return false if the file is not an image or is not accessable, otherwise it will return an array...

<?php
$file
= 'somefile.jpg';

# assuming you've already taken some other
# preventive measures such as checking file
# extensions...

$result_array = getimagesize($file);

if (
$result_array !== false) {
  
$mime_type = $result_array['mime'];
   switch(
$mime_type) {
       case
"image/jpeg":
           echo
"file is jpeg type";
           break;
       case
"image/gif":
           echo
"file is gif type";
           break;
       default:
           echo
"file is an image, but not of gif or jpeg type";
   }
} else {
   echo
"file is not a valid image file";
}
?>

using this function along with others mentioned on this page, image ploading can be made pretty much fool-proof.

See http://php.net/manual/en/function.getimagesize.php for supported image types and more info.
olijon, iceland
18-Jun-2004 10:24
When uploading large images, I got a "Document contains no data" error when using Netscape and an error page when using Explorer. My server setup is RH Linux 9, Apache 2 and PHP 4.3.

I found out that the following entry in the httpd.conf file was missing:

<Files *.php>
  SetOutputFilter PHP
  SetInputFilter PHP
  LimitRequestBody 524288 (max size in bytes)
</Files>

When this had been added, everything worked smoothly.

- Oli Jon, Iceland
brion at pobox dot com
10-May-2004 08:08
Note that with magic_quotes_gpc on, the uploaded filename has backslashes added *but the tmp_name does not*. On Windows where the tmp_name path includes backslashes, you *must not* run stripslashes() on the tmp_name, so keep that in mind when de-magic_quotes-izing your input.
steve dot criddle at crd-sector dot com
16-Apr-2004 01:43
IE on the Mac is a bit troublesome.  If you are uploading a file with an unknown file suffix, IE uploads the file with a mime type of "application/x-macbinary".  The resulting file includes the resource fork wrapped around the file.  Not terribly useful.

The following code assumes that the mime type is in $type, and that you have loaded the file's contents into $content.  If the file is in MacBinary format, it delves into the resource fork header, gets the length of the data fork (bytes 83-86) and uses that to get rid of the resource fork.

(There is probably a better way to do it, but this solved my problem):

<?php
if ($type == 'application/x-macbinary') {
   if (
strlen($content) < 128) die('File too small');
  
$length = 0;
   for (
$i=83; $i<=86; $i++) {
      
$length = ($length * 256) + ord(substr($content,$i,1));
         }
  
$content = substr($content,128,$length);
}
?>
hisham
02-Mar-2004 12:54
On a similar note to jim dot dam at sympatico dot ca 27-Feb-2002 09:13

Browsers intepret png upload type differently too eg.

print_r() output from Mozilla 1.6
Array ( [name] => eg1.png [type] => image/png [tmp_name] => /var/tmp/phpIJd4FL [error] => 0 [size] => 66614 )

print_r() output from IE 6
Array ( [name] => eg1.png [type] => image/x-png [tmp_name] => /var/tmp/phpHJ04Dh [error] => 0 [size] => 66614 )

Note the difference of image/png and image/x-png type intepretation of the same image file.

Further note:
http://www.cti.ecp.fr/documents/tests/png.html
~caetin~ ( at ) ~hotpop~ ( dot ) ~com~
10-Feb-2004 10:37
From the manual:

     If no file is selected for upload in your form, PHP will return $_FILES['userfile']['size'] as 0, and $_FILES['userfile']['tmp_name'] as none.

As of PHP 4.2.0, the "none" is no longer a reliable determinant of no file uploaded. It's documented if you click on the "error codes" link, but you need to look at the $_FILES['your_file']['error']. If it's 4, then no file was selected.
maya_gomez ~ at ~ mail ~ dot ~ ru
06-Feb-2004 07:20
when you upload the file, $_FILES['file']['name'] contains its original name converted into server's default charset.
if a name contain characters that aren't present in default charset, the conversion fails and the $_FILES['file']['name'] remains in original charset.

i've got this behavior when uploading from a windows-1251 environment into koi8-r. if a filename has the number sign "¹" (0xb9), it DOES NOT GET CONVERTED as soon as there is no such character in koi8-r.

Workaround i use:

<?php
if (strstr ($_FILES['file']['name'], chr(0xb9)) != "")
{
  
$_FILES['file']['name'] = iconv (
      
"windows-1251",
      
"koi8-r",
      
str_replace (chr(0xb9), "N.", $_FILES['file']['name']));
};
?>
srikanth at ideaworks3d dot com
19-Jan-2004 12:18
If the file is empty (0 bytes) it is treated as if no file
is uploaded. $_FILES['userfile']['tmp_name'] returns "none".
Shekhar Govindarajan
26-Oct-2003 07:38
To upload large files, besides setting upload_max_filesize, you must also set post_max_size in php.ini or using ini_set() function. Keep the value to more than the maximum expected size of the upload. This is because, you may be sending other post data along with the upload file. For example:

post_max_size = 601M

This should be a safe setting if you want to upload files of around 600 MB (as specified by upload_max_filesize = 600M)

While uploading large files, you should also increase the values for max_execution_time  and max_input_time directives. Else your script will timeout or timeout before being able to parse the entire input/uploaded data.
therebechips
06-Sep-2003 04:02
Re: Handling uploads and downloads of large files and storing in MySQL.

Use two tables to store data about the file and the file data itself. ***Important: to preserve the integrity of the data use base64_encode() NOT addslashes().

<?php
// Max packet size
  
define("MAX_SQL",50000);
  
$filehandle = fopen($tmp, "rb") or die( "Can't open file!" );
  
$query=    "INSERT INTO files (name, type, size) VALUES(".
            
$DB->quote($name).", ".
            
$DB->quote($type).", ".
            
$DB->quote($size).
            
")";

  
// Execute Query
  
$result = $DB->query($query);
  
$file_id = mysql_insert_id();

// Copy the binary file data to the filedata table in sequential rows each containing MAX_SQL bytes
// Your table should have an index set to auto_increment
// Store the file_id to identify the data fragments
  
while (!feof ($filehandle)) {
      
$data = base64_encode(fread($filehandle,MAX_SQL));
      
$query = "INSERT INTO filedata (file_id, data) VALUES($file_id,\"".$data."\")";
      
$result = $DB->query($query);
   }
  
fclose ($filehandle);
?>

Decode the data fragments and recombine them:
<?php
   $file_id
=$_GET ['file_id'];
  
$query ="select file_id, name, type, size from files where file_id='$file_id'";
  
$result = $DB->query($query);
  
$row= mysql_fetch_array ($result);
  
$type = $row ["type"];
  
$name = $row ["name"];
  
$size = $row ["size"];
  
$file_id = $row ["file_id"];

  
// get the file data
  
$query = "select id, data from filedata where file_id='$file_id' ORDER by id";
  
$result = $DB->query($query);

// decode the fragments and recombine the file
  
$data = "";
   while (
$row = mysql_fetch_array($result)) {
      
$data .= base64_decode($row ["data"]); 
   }
  
// output the file
  
header ("Content-type: $type");
  
header ("Content-length: $size");
  
header ("Content-Disposition: attachment; filename=$name");
  
header ("Content-Description: PHP Generated Data");
   echo
$data;
?>
e4c5 at raditha dot com
12-Aug-2003 04:22
Progress bar support has been a recurring theme in many a PHP mailing list over the years. You can find a free progress monitor component for PHP file uploads at http://sourceforge.net/projects/megaupload/

The advantage of this system is that you do not have to apply any patches to PHP to make use of it.
user at php dot net
26-Jul-2003 02:16
To add an example to the bart at combodata dot nl's note:

When you're using this type of form:

<form name="" method="post" action="dostuff.php" enctype="multipart/form-data" >
<input type="file" name="dat[foto]" />
<input type="file" name="dat[banner]" />
<input type="file" name="dat[pdf]" />
</form>

You can refer to the uploaded files this way:
$_FILES["dat"]["tmp_name"]["foto"]
name: $_FILES["dat"]["name"]["foto"]
size: $_FILES["dat"]["tmp_name"]["foto"]
mitchy_AT_spacemonkeylabs_DOT_com
17-Jun-2003 11:29
After hours of profanity-laced tirades and futility mixed with panic, I have found the solution to my ills:  HTTP_Upload, written by Thomas V.V. Cox, found at:

http://pear.php.net/HTTP_Upload

First, it is done in PEAR[1], and you all should use PEAR too.  Second, it has many additions, like creating unix-friendly filenames from those wacky Windows users (filenames with spaces, yuck!)...

I cut-n-pasted from the example page, and it just plain worked.  No muss, no fuss, no messy applicator brush!

[1]What is PEAR?  It is a means for PHP developers to share and reuse code, usually reducing development costs and efforts by an order of magnitude.  Even more important, someone else figured out that blasted library of functions that have had you stumped for days, and it is wrapped up in a neat, easy-to-use object.  With a simple command-line installer, PEAR makes getting PHP-based add-ons and libraries as easy as apt-get!  It is found at http://pear.php.net/
diegoful at yahoo dot com
25-Mar-2003 02:22
SECURITY CONSIDERATION: If you are saving all uploaded files to a directory accesible with an URL, remember to filter files not only by mime-type (e.g. image/gif), but also by extension. The mime-type is reported by the client, if you trust him, he can upload a php file as an image and then request it, executing malicious code.
I hope I am not giving hackers a good idea anymore than I am giving it to good-intended developers. Cheers.
garyds at miraclemedia dot ca
15-Mar-2003 08:12
As it has been mentioned above, Windows-based servers have trouble with the path to move the uploaded file to when using move_uploaded_file()... this may also be the reason copy() works and not move_uploaded_file(), but of course move_uploaded_file() is a much better method to use. The solution in the aforementioned note said you must use "\\" in the path, but I found "/" works as well. So to get a working path, I used something to the effect of:

"g:/rootdir/default/www/".$_FILES['userfile']['name']

...which worked like a charm.

I am using PHP 4.3.0 on a win2k server.

Hope this helps!
ov at xs4all dot nl
08-Mar-2003 09:08
This took me a few days to find out: when uploading large files with a slow connection to my WIN2K/IIS5/PHP4 server the POST form kept timing out at exactly 5 minutes. All PHP.INI settings were large enough to accomodate huge file uploads. Searched like hell with keywords like "file upload php timeout script" until I realised that I installed PHP as CGI and added that as a keyword. This was the solution:

To set the timeout value:
1. In the Internet Information Services snap-in, select the computer icon and open its property sheets.
2. Under Master Properties, select WWW Service, and then click the Edit button
3. Click the Home Directory tab.
4. Click the Configuration button.
5. Click the Process Options tab, and then type the timeout period in the CGI Script Timeout box.
mccorkle+php at devteam dot org
08-Jan-2003 01:59
To anyone that is trying to use values="foo" to set a default value in a input type of ``file'', I found this out from http://www.blooberry.com/indexdot/html/tagpages/i/inputfile.htm:

*  Internet Explorer, Netscape and Opera do not use the VALUE attribute as the default contents of the input area. Any default value set via HTML is not usable via scripting and the DOM as well (hence it is not listed as 'supported' in any of the browsers.) If a user enters text in the field however, that value is then reachable via the DOM as it normally would be for a normal INPUT field (via the .value property.) The reason for this behavior would presumably be to ensure the security/safety of users against malicious authors.

Tooke me a bit to find this, so I figured I'd share.
panayotis at yellownetroad dot com
18-Dec-2002 05:21
In order to enable $HTTP_RAW_POST_DATA, you can set always_populate_raw_post_data to On either in php.ini or .htaccess
travis dot lewis at amd dot com
04-Dec-2002 02:58
If you we dumb like me you installed Redhat 8.0 and kept the default install of packages for Apache 2.0 and PHP4.2.2.  I could not upload any files larger than 512kB and all the php directorives were set to 32MB or higher.
memory_limit = 128M
post_max_size = 64M
upload_max_filesize = 32M

And my upload web page was set to 32MB as well:
<Form ID="frmAttach" Name="frmAttach" enctype="multipart/form-data" action="attachments.php" method="POST">
<input type="hidden" name="MAX_FILE_SIZE" value="33554432" />

However, the insiduous php.conf (/etc/httpd/conf.d/php.conf) file used by default RPM install of Redhat httpd has a LimitRequestBody set to 512kB ("524288" ).  Adjusting this to 32MB ("33554432") got things going for the larger files.  Here is my php.conf file in its entirety.  Hope this helps someone.  L8er.

#
# PHP is an HTML-embedded scripting language which attempts to make it
# easy for developers to write dynamically generated webpages.
#

LoadModule php4_module modules/libphp4.so

#
# Cause the PHP interpreter handle files with a .php extension.
#
<Files *.php>
   SetOutputFilter PHP
   SetInputFilter PHP
   LimitRequestBody 33554432
</Files>

#
# Add index.php to the list of files that will be served as directory
# indexes.
#
solja at gci dot net
03-May-2002 08:11
Just another way I found to keep an uploaded file from overwriting an already exisiting one - I prefix each uploaded file with time() timestamp. Something like:
$unique_id = time();

Then when I give the new name to move the file to, I use something like:
$unique_id."-".$filename

So I get a fairly unique filename each time a file is uploaded. Obviously, if two files are uploaded at once, both will have the same timestamp, but I don't worry too much about that. Hope this might help someone.
am at netactor dot NO_SPAN dot com
15-Mar-2002 12:20
Your binary files may be uploaded incorrectly if you use modules what recode characters. For example, for Russian Apache, you should use
<Files ScriptThatReceivesUploads.php>
CharsetDisable On
</Files>

<Dealing with XFormsError Messages Explained>
 Last updated: Thu, 19 May 2005
Copyright © 2001-2005 The PHP Group
All rights reserved.
This unofficial mirror is operated at: The Server Pages
Last updated: Thu May 19 17:35:34 2005 CDT