PHP  
downloads | documentation | faq | getting help | mailing lists | reporting bugs | php.net sites | links | my php.net 
search for in the  
<CookiesError Messages Explained>
view the version of this page
Last updated: Thu, 21 Aug 2003

Chapter 18. Handling file uploads

POST method uploads

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). 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.

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

Note that 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.

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

Example 18-1. File Upload Form

<form enctype="multipart/form-data" action="_URL_" method="POST">
<input type="hidden" name="MAX_FILE_SIZE" value="30000">
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. Also, be sure your file upload form has enctype="multipart/form-data" otherwise the file upload will not work.

Warning

The MAX_FILE_SIZE is advisory to the browser. It is easy to circumvent this maximum. So don't count on it that the browser obeys your wish! The PHP-settings for maximum-size, however, cannot be fooled. But you should add MAX_FILE_SIZE anyway as it saves users the trouble to wait for a big file being transfered only to find out that it was too big afterwards.

The Variables defined for uploaded files differs depending on the PHP version and configuration. The autoglobal $_FILES exists as of PHP 4.1.0 The $HTTP_POST_FILES array has existed since PHP 4.0.0. These arrays will contain all your uploaded file information. Using $_FILES is preferred. If the PHP directive register_globals is on, related variable names will also exist. register_globals defaults to off as of PHP 4.2.0.

The contents of $_FILES from our example script 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. ['error'] was added in PHP 4.2.0

Note: In PHP versions prior 4.1.0 this was named $HTTP_POST_FILES and it's not an autoglobal variable like $_FILES is. PHP 3 does not support $HTTP_POST_FILES.

When register_globals is turned on in php.ini, additional variables are available. For example, $userfile_name will equal $_FILES['userfile']['name'], $userfile_type will equal $_FILES['userfile']['type'], etc. Keep in mind that as of PHP 4.2.0, register_globals defaults to off. It's preferred to not rely on this directive.

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 18-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 earlier then 4.1.0, $HTTP_POST_FILES should be used instead of
// $_FILES.  In PHP earlier then 4.0.3, use copy() and is_uploaded_file()
// instead of move_uploaded_file

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

print "<pre>";
if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {
    print "File is valid, and was successfully uploaded. ";
    print "Here's some more debugging info:\n";
    print_r($_FILES);
} else {
    print "Possible file upload attack!  Here's some debugging info:\n";
    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.

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



add a note add a note User Contributed Notes
Handling file uploads
Shekhar Govindarajan
26-Oct-2003 09: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.
zilinex at linuxmail dot org
26-Oct-2003 07:55
a cool function for uploading arbitrary number of files:

function upload_files($path,$overwrite=NULL,$names=NULL){
        global $_FILES;

        foreach($_FILES as $key => $FILE){
                if( empty($FILE['name'])
                   || ( empty($overwrite) && file_exists($path."/".basename($FILE['name'])) ) )continue;
                if( !copy($FILE['tmp_name'],
                          $path."/". (empty($names[$key])?basename($FILE['name']):basename($names[$key])) ))
                        echo "<br>Fail to copy ".$FILE['tmp_name'] ;
        }
}
heivio at hotmail dot com
09-Sep-2003 02:37
To upload huge file to any BLOB to mySQL database, there is a simplest solution with only one special requirement. You must have the right to use command {LOAD_FILE("<FilePathname>")}. <FilePathname> must be composed with '/' (forward slashes) or you may use 'str_replace(str,str,str)' to replace the backward ('\') slashes for windows platform.

e.g.
C:\WINDOWS\TEMP\php2064.TMP (pathname backward slashes)
C:/WINDOWS/TEMP/php2064.TMP (pathname with forward slashes)

Example:
(Assume you have asscess right to "LOAD_FILE()" in mySQL)

<?php
   
//establish database connection using user defined class.
   
$db = new dbConnection();
   
$link $db->connect();

   
$fileName $_FILES['image']['tmp_name'];
   
$imgLocation str_replace("\\","/"$fileName); 
   
/* '\\' represent '\' */

   
echo $imgLocation;
       
$rval['update'] = mysql_query("UPDATE Users
                                      SET photo = LOAD_FILE(
\"$imgLocation\")
                                      WHERE userid = 'foo' "
,$link)
                                      or 
                                      die (
mysql_error($link));

   
//commit is used , just in case.
   
$commit mysql_query('commit');
   
mysql_close($link);

?>
therebechips
07-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);
   
$rowmysql_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
13-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
27-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"]
chernyshevsky at hotmail dot com
01-Jul-2003 10:18
Tip: Use extract() to quickly place values from $_FILE[] into local variables.

 
extract($_FILES['userfile'], EXTR_PREFIX_ALL, 'uf');
// you now have name, tmp_name, size, and type in $uf_name, $uf_tmp_name, $uf_size, $uf_type
haller at fh-weingarten dot de
27-Jun-2003 03:05
for multiple file uploads at once, you can use multiple <input type=file> tags.

note that rfc1867 allows multiple file selection for each file-tag, but nearly no browser is supporting it.

for that reason, there is an java applet:
http://www.gigablast.com/search?q=jupload+applet
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/
Dave
26-Apr-2003 12:56
Large file Uploading

If you use apache like my company, and need to upload large files, make sure you change the limitrequestbody option in apache. otherwise, you wont even be able to post.
Richard
18-Apr-2003 06:51
If you're using Apache 2, you may have to comment out the following lines in your Apache config

SetOutputFilter PHP
SetInputFilter PHP

With these in, my binary files were incorrectly uploaded
timon at fixproject dot com
13-Apr-2003 12:30
This is my simple image upload script which manages a few things :

Renames the image when copying from temp/folder to upload/folder. I Used the user_id($id) which almost every good
programmer would use. This way 2 files can never have an name issue (overwrite).....

It checks the image matches the maximum width and height...

It checks if the image matches the maximum size rules

I think this can be useful for some specified webmasters :

// Script  *************************************

$imtype=$_FILES["imgfile"]["type"];
$imsize=$_FILES["imgfile"]["size"];
$imname=$_FILES["imgfile"]["name"];
$imtemp=$_FILES['imgfile']['tmp_name'];
$immaxsize="50000";
$immaxwidth="700";
$immaxheight="700";

if ($imsize > $immaxsize){
    $error_message="You Image is $imsize Bytes the maximum allowed is $immaxsize";
}

if (!$error_message){
    $imgsize = GetImageSize($imgfile);
   //== check size  0=width, 1=height
    if (($imgsize[0] > $immaxwidth) || ($imgsize[1] > $immaxheight)){
        $error_message="Your image width and height allowed is $immaxwidth by $immaxheight px. Yours did not match this rule";
    }
}// end if error_message
if (!$error_message){
    if ($imtype == "image/x-png"){
        $extension=".png";
    }elseif ($imtype == "image/pjpeg"){
        $extension=".jpg";
    }elseif ($imtype == "image/gif"){
        $extension=".gif";
    }else{
        $error_message="Please upload images with the extension .jpg or .jpeg or .gif or .png only";
    }
}//end if error_message
if (!$error_message){
    if (is_uploaded_file($imtemp)){
           copy($imtemp,"$uploaddir/".$id."$extension");
           unlink($imtemp);
        } else {
           $error_message="You did not match our Security Settings !";
       }
}// END IF ERROR MESSAGE IS EMPTY & PICTURE SETTINGS

if ($error_message){
require ("$dir[func]/error_check.php");
error_check($error_message);
exit;
}else{
profile_save_message();
}

// End Script ************************************
mightye at mightye dot org
08-Apr-2003 05:53
On Handling uploads of large files on Windows and/or storing in MySQL.

I see a couple of people suggest you do this:
$data = addslashes(fread(fopen($file['tmp_name'], "r"), ($file['size'])));

First, on Windows, you want "r" to be "rb" because the file may be binary.  Second, if this is a large file, this is a highly inefficient script.  Plus if you are storing the file in MySQL, with a large file, your query will more than likely exceed the MySQL default max size, and you will get the "MySQL has gone away" error.

What I suggest is that you handle a file in parts, with a table for the file name, mime type, file size, etc, and a separate table to hold the actual file data.  Then do something like this:

define("MAX_SQL",50000);
$fp = fopen($file['tmp_name'],"rb");//
$sql = "INSERT INTO files (`filename`,`mimetype`,`filesize`)
VALUES
('$file[name]', '$file[type]','$file[size]')";
mysql_query($sql) or die(mysql_error(LINK));
$id = mysql_insert_id();
while (!feof($fp)){
    $data = addslashes(fread($fp,MAX_SQL));
    $sql = "INSERT INTO filedata (fileid,filedata) VALUES($id,\"".$data."\")";
    mysql_query($sql) or die(mysql_error(LINK));
}

You will want an auto incrementing field in `filedata` as you will need to order by this field to ensure that the file is reassembled in the correct order, or else add an incrementing ID in your script.

This model is many orders of magnitude more efficient than attempting to load an entire large upload in to memory all at once, and generating a huge SQL statement.  The "b" switch ensures that in Windows we don't hit a premature end of file, and the fact that we are only loading portions of the file in to memory at a time, and are passing only portions of it to the database at a time represents a significantly lower load on the server both processor wise and memory wise. 

In my testing, this later model required 5 seconds of processing time for a 100 meg file after the upload was complete, while other models timed out at my 120 second threshold before the file was completely loaded in to memory.
diegoful at yahoo dot com
25-Mar-2003 04: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
16-Mar-2003 10: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!
kern at epiware dot com
13-Mar-2003 10:58
Below is a script that will help to resolve the most coomon prolbems that are encountered when trying to to upload large files. 

http://www.epiware.com/scripts/upload.php

Just save the script to your directory, and it will display your current PHP settings, and offer suggestions to increase your file upload size.  You can then also test the file upload process with the same script to verify.  Just tested on Linux not Windows
ov at xs4all dot nl
09-Mar-2003 11: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 03: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
19-Dec-2002 07: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
05-Dec-2002 04: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.
#
rynop2000 at hotmail dot com
20-Jul-2002 12:57
--HANDLING LARGE FILE UPLOADS and entering into a MySQL blob field--

Hope this helps someone as it was hard to fig out.  MySQL default buffer and packets are set small.  here is a method to get aroudn it.

$filehandle = fopen($addfile, "r");
$filesize = filesize($addfile);

//now this is bullshit, but have to read the file piece by piece and insert because the mysql server is set up to only handle 1meg inserts (small buffer and packet).

$buffer = addslashes(fread($filehandle, 906240));
$query = "Insert into files (file, filename, size, userid ) values ('$filedata','$addfile_name', '$filesize')";
$this->query($query);
$id = mysql_insert_id($this->link_id());
       
while (!feof ($filehandle)) {
  $buffer = addslashes(fread($filehandle, 906240));
  $query = "UPDATE files SET file = concat('$buffer') where id='$id'";
  $this->query($query);
}
fclose ($filehandle);
solja at gci dot net
04-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 02: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>
jim dot dam at sympatico dot ca
27-Feb-2002 12:13
Some browsers label the type differently.  I found this out while trying to restrict the file types that could be uploaded.  I created a simple script that will let the user upload a file, and then print_r() the $_FILES variables.  In Microsoft Internet Explorer 6.0.26 on Windows XP, I got this result:

Array
(
    [file] => Array
        (
            [name] => command.com
            [type] => application/octet-stream
            [tmp_name] => C:\WINDOWS\TEMP\phpBE.tmp
            [size] => 50620
        )

)

Uploading the same file with the same script in Opera 6.01 (again on Windows XP), I got this result:

Array
(
    [file] => Array
        (
            [name] => command.com
            [type] => application/octet-stream; name="command.com"
            [tmp_name] => C:\WINDOWS\TEMP\phpBF.tmp
            [size] => 50620
        )

)

The tmp_name is obviously supposed to be different, but notice how the name of the file was appened to type.  My solution is either (a) split() at the semicolon and use the first element of that array, or (b), search the type for the allowed filetypes (via strstr()).
17-Jan-2002 05:09
This example is was a bit confusing for me... didn't get a file to upload till I looked here.
http://www.faqts.com/knowledge_base/view.phtml/aid/988/fid/62

<CookiesError Messages Explained>
 Last updated: Thu, 21 Aug 2003
show source | credits | sitemap | mirror sites 
Copyright © 2001-2003 The PHP Group
All rights reserved.
This mirror generously provided by: http://php.mirrors.ilisys.com.au/
Last updated: Sat 01 Nov 2003 04:13:36 EST EST