Delicious Bookmark this on Delicious Share on Facebook SlashdotSlashdot It! Digg! Digg



PHP : Function Reference : Filesystem Functions : move_uploaded_file

move_uploaded_file

Moves an uploaded file to a new location (PHP 4 >= 4.0.3, PHP 5)
bool move_uploaded_file ( string filename, string destination )

This function checks to ensure that the file designated by filename is a valid upload file (meaning that it was uploaded via PHP's HTTP POST upload mechanism). If the file is valid, it will be moved to the filename given by destination.

This sort of check is especially important if there is any chance that anything done with uploaded files could reveal their contents to the user, or even to other users on the same system.

Parameters

filename

The filename of the uploaded file.

destination

The destination of the moved file.

Return Values

If filename is not a valid upload file, then no action will occur, and move_uploaded_file() will return FALSE.

If filename is a valid upload file, but cannot be moved for some reason, no action will occur, and move_uploaded_file() will return FALSE. Additionally, a warning will be issued.

Notes

Note:

move_uploaded_file() is both safe mode and open_basedir aware. However, restrictions are placed only on the destination path as to allow the moving of uploaded files in which filename may conflict with such restrictions. move_uploaded_file() ensures the safety of this operation by allowing only those files uploaded through PHP to be moved.

Warning:

If the destination file already exists, it will be overwritten.

See Also
is_uploaded_file()
See Handling file uploads for a simple usage example

Related Examples ( Source code ) » move_uploaded_file



Code Examples / Notes » move_uploaded_file

04-nov-2005 11:56

[quote]
Note that post_max_size also needs to be considered, by default it is 8M. I raised my upload_max_filesize to 20M and was wondering why 10M uploads weren't working...
[/quote]
It could be because of your max execution time.


iim dot vxk

when you get this 2 Warnings - paths are a real sample - ::
-
move_uploaded_file(/uploads/images/sample.png) [function.move-uploaded-file]: failed to open stream: No such file or directory in /scripts/php/system/upload-file.php on line X
-
and
-
move_uploaded_file() [function.move-uploaded-file]: Unable to move '/tmp/somefilename' to '/uploads/images/sample.png' in /scripts/php/system/upload-file.php on line X
-
probably the path '/uploads/images/sample.png' is incomplete, in my case the complet path is "/home/x-user/public_html/uploads/images/sample.png"
you can use getcwd() to know the current working directory.
:)


mail

Warning: If you save a md5_file hash in a database to keep record of uploaded files, which is usefull to prevent users from uploading the same file twice, be aware that after using move_uploaded_file the md5_file hash changes! And you are unable to find the corresponding hash and delete it in the database, when a file is deleted.

info

Values upload_max_filesize and post_max_size (ie. php.ini values) cannot be modified in runtime with ini_set() function.
If you are using Apache web server, use .htaccess files with an IfModule replacing values corresponding to your file size and PHP version:
<IfModule mod_php4.c>
php_value upload_max_filesize 50M
php_value post_max_size 50M
</IfModule>
- means 50MB upload limit.


calamitoso

to separate (for example) images from other file types among the uploaded files you can check the MIME type also (thus making the file extension check unnecessary)
$temp = strpos($_FILES["pic"]["type"], "image");
if ($rep===FALSE){
  //the strpos function will return a boolean "false" ONLY if the needle string is not found within the haystack
  echo "is not an image";
}else{
  echo "is an image";
}


adam

To retrieve the file extension, I think this example makes more sense than the one below.  
$ext = explode(".", $file);
$ext = array_pop($ext);
It doesn't have to count() the array and then subtract 1 to point to the proper array element, it simply isolates the last element of the array, and discards everything else.


j dot m dot thomas

To retrieve the file extension, and various other information about the path, it is easiest to use the pathinfo function.
<?php
$path_parts = pathinfo('/www/htdocs/index.html');
echo $path_parts['dirname'], "\n";
echo $path_parts['basename'], "\n";
echo $path_parts['extension'], "\n";
?>
Would produce:
/www/htdocs
index.html
html
http://uk.php.net/manual/en/function.pathinfo.php


mancow

To nouncad at mayetlite dot com,
That function will work fine for files with a 3-character file extension.  However, it is worth noting that there are valid, registered file extensions that are longer than 3 characters.  For example, a JPEG file can be denoted by *.jpg (and others), but it can also have *.jpeg as a valid extension.  Check out http://www.filext.com/ for a good reference of file extensions.
The best bet to me would be parsing the uploaded file's name ($_FILES['uploadedfile']['name']) based on the presence of dots.  Another wrench in the gears:  a file can have dots in the filename.  That's easy enough to handle -- just explode() the file name and hope that the last element in the array it gives you is the file extension (you can always validate it if you're so inclined).  Then just piece it together in a string accordingly by stepping through the array (don't forget to add those dots back to where they were!), appending a guaranteed unique string of characters (or enumerate it like you were doing, keeping track via a loop), and finally tacking on the file extension.
You may have other mechanisms for verifying a file's extension, such as a preg_match on the whole name, using something like "/\\.(gif|jpg|jpeg|png|bmp)$/i" (more can, of course, be added if you so desire) for the most common types of images found on the web.
For blindly guaranteeing an uploaded file will be uniquely named, this seems like a fantastic way to go.  Enjoy!


13-mar-2007 10:55

To allow bigger upload file size, edit in php.ini and change:
upload_max_filesize =350M ; Maximum allowed size for uploaded files.
post_max_size =360M ; Maximum size of POST data that PHP will accept.
max_execution_time =5000 ; Maximum execution time of each script, in seconds
In adition, you must change the server configuration as well.
For apache server, edit httpd.conf.


allen

The first comment totally threw me off. Under the 'new regime', the 'string filename' is $_FILES['userfile']['tmp_name']
Also note that the 'string destination' should be the full path and filename. As long as your server isnt using virtual hosting, you should be able to use $_SERVER['DOCUMENT_ROOT'] . "path/within/website". This'll save hours of hassle trying to get sometimes ignorant ISPs to give you your full and 'no symlinks' path.
Allen


09-nov-2003 03:54

The example to find file extension bellow is quite confusing and its using to much code for a much simpler solution. Which is in example:
$file_parts = pathinfo('dir/' . $_FILES['file']['name']);
$file_extension = strtolower($file_parts['extension']);
The 'dir/' part is only to get a valid path.


harmor

the dot only dot storm at gmail dot com wrote:
>
> In addition to the file extension checking. A simply way
> of getting the extension (regardless of size):
>
> $efilename = explode('.', $filename);
> $ext = $efilename[count($efilename) - 1];
>
How about:
$ext = end(explode('.',$filename));


sergeygrinev

small typo:
$fulldest = $dest.$newfilename;
show be
$fulldest = $dest.$filename;
or you would have infinite loop.


ffproberen2

On windows I made the directory writable, by changing the Apache httpd.conf file.
The problem I had, was with the upload directory. The move_uploaded_file produced an error like: failed to open stream: Permission denied.
I changed my php.ini to specify an upload directory:
 upload_tmp_dir = "d:/temp/php/uploads/"
and I added the following in the Apache hpptd.conf file:
 <Directory "D:/temp/php/uploads">
   Options None
   AllowOverride None
   Order allow,deny
   Allow from all
 </Directory>
restarted Apache, and the upload succeeded.


allan666

On the Fedora Core 3 Linux distribution, you may get a "failed to open stream: Permission denied in ..." message. I fact changing the permission of the directory will not work (even if you set to 0777). It is because of the new SELinux kernel that allow apache user to write only in /tmp dir (I think). In order to solve the problem you must to disable the SELinux (at least for apache service) to allow the server to write in other directories. To do that, run the system-config-securitylevel app and disable the SE to apache service. Reboot your system and continue your work. Hope it helps!

zarel

nouncad at mayetlite dot com posted a function that uploaded a file, and would rename it if it already existed, to filename[n].ext
It only worked for files with extensions exactly three letters long, so I fixed that (and made a few other improvements while I was at it).
<?php
// Usage: uploadfile($_FILE['file']['name'],'temp/',$_FILE['file']['tmp_name'])
function uploadfile($origin, $dest, $tmp_name)
{
 $origin = strtolower(basename($origin));
 $fulldest = $dest.$origin;
 $filename = $origin;
 for ($i=1; file_exists($fulldest); $i++)
 {
   $fileext = (strpos($origin,'.')===false?'':'.'.substr(strrchr($origin, "."), 1));
   $filename = substr($origin, 0, strlen($origin)-strlen($fileext)).'['.$i.']'.$fileext;
   $fulldest = $dest.$newfilename;
 }
 
 if (move_uploaded_file($tmp_name, $fulldest))
   return $filename;
 return false;
}
?>


29-oct-2005 04:44

Note that post_max_size also needs to be considered, by default it is 8M. I raised my upload_max_filesize to 20M and was wondering why 10M uploads weren't working...

fabric9

Never mind guys, I solved it. Stupid error, don't even ask :P

albert

move_uploaded_file()'s return codes are not allways obious !
Unable to move '/var/tmp/phpuuAVJv' to '/home/me/website.com/upload/images/hello.png'
will apear if your disk is full, or the webserver (www user) exeeded it's disk qouta. (probably some others)
i dont know if its a bug (just not iplemented) or a feature (to hide from 3rd parties details about the system or about the specific error) ?
it happend to me that after several months of successful operation, the disk filled up and qouta exeeded.
it took me long time, finding out why all the sudden my scripts didnt work properly anymore.


darrell

move_uploaded_file apparently uses the root of the Apache installation (e.g. "Apache Group\Apache2" under Windows) as the upload location if relative pathnames are used.
For example,
$ftmp = $_FILES['userfile']['tmp_name'];
$fname = $_FILES['userfile']['name'];
move_uploaded_file($ftmp, $fname);
                         
moves the file to
"Apache Group\Apache2\$fname";
In contrast, other file/directory related functions use the current directory of the php script as the offset for relative pathnames.  So, for example, if the command
mkdir('tmp');
is called from 'Apache Group\Apache2\htdocs\testpages\upload.php', the result is to create
'Apache Group\Apache2\htdocs\testpages\tmp'
On the other hand, if 'mkdir' is called just before 'move_uploaded_file', the behavior changes.  The commands,
mkdir('tmp');
move_uploaded_file($ftmp, $fname);
used together result in
"Apache Group\Apache2\htdocs\testpages\tmp\$fname"
being created.  Wonder if this is a bug or a feature.
Darrell


ineedmynetwork.com

Microsoft returns image/pjpeg not image/jpg when using $_FILES['imageName']['type'];

froid_nordik

Make sure the directory you are moving the file to exists before using this command.

nlgordon

Just a helpful comment.  If you have open_basedir set then you must set upload_tmp_dir to somewhere within the open_basedir.  Otherwise the file upload will be denied.  move_uploaded_file might be open_basedir aware, but the rest of the upload process isn't.

jest3r

It seems that move_uploaded_file use the GROUP permissions of the parent directory of the tmp file location, whereas a simple "copy" uses the group of the apache process. This could create a security nighmare if your tmp file location is owned by root:wheel

andrew@euperia,com

Instead of using chdir or chmod 0777 a safer alternative to move_uploaded_files is to use PHP's ftp functions to move the file into a web dir.
1. Make ftp connection to 127.0.0.1 with the correct username and password.
2. ftp_chdir to the required directory.
3. ftp_put ($_FILES['myfile']['tmp_name'], $finalfilename);
4. ftp quit.


jake@lyrehc

In response to bogusred,
The user and group PHP runs as is not always 'nobody', but is environment dependant. Typically Apache runs as root and changes into another user as specified in the httpd.conf.  'nobody' is a common default setting, but 'apache' or another user is equally likely and in a shared hosting environment you often find it runs as one of various users depending on the virtual host.


shacker

If you're dealing with files uploaded through some external FTP source and need to move them to a final destination, searching php.net for "mv" or "move" won't get you what you want. You want the rename() function.
http://www.php.net/manual/en/function.rename.php
(move_uploaded_file() won't work, since the POST vars won't be present.)


nibinaear

If you want to change the filename used by the user then use this code (static name). This example uploads a pdf magazine to a website and always overwrites the file that is there. Use if you are dealing with a small number of files or people who know what they're doing!
<?php
 if(!empty($_FILES["magfile"]))
 {
   $uploaddir = $_SERVER['DOCUMENT_ROOT']."/dainsider/magazines/";
   $uploaddir.="magazine.pdf";
   
   //Copy the file to some permanent location
   if(move_uploaded_file($_FILES["magfile"]["tmp_name"], $uploaddir))
   {
     echo "Magazine Updated!";
   }
   else
   {
     echo "There was a problem when uploding the new file, please contact ".$admin_email." about this.";
     print_r($_FILES);
   }
 }
?>


bogusred

If you have a directory in a *nix environment where you store all of your file uploads and your php script only seems to work when permissions for that directory are set to 777, here's how to fix it so that you can have the security benefits of 755 while still allowing your php scripts to work, including the move_uploaded_file().
through shell access, navigate to the directory that contains your uploads folder and run the following 2 commands:
chown -R nobody uploaddir
chmod -R 755 uploaddir
Replace 'uploaddir' with the name of your uploads directory. The first command changes the owner of the directory and files to 'nobody' which is what php operates under. The second changes the folder and files to only allow user access to writing. This is much more secure.
Hopefully this will help someone out there who had the same problem as me.


03-feb-2007 03:32

If you find that large files do not upload in PHP even though you've changed the max_upload_size , this is because you need to change the max memory size varible too. The entire file is loaded into memory before it is saved to disk.

php

If you are building an intranet framework and use NAT/Routing heed the following advice.
If you want to move uploaded files to an FTP server you cannot use the ftp wrapper (ie. 'ftp://user:pass@ftpserver/') as part of your move_uploaded_file() action.  This is due to the wrapper only using passive mode with ftp.
The only workaround is using the ftp functions (may not be compiled by default with *nix but is by default with windows).


mikelone

If the user try to upload a too bigger file then the upload procedure will fail even if u have established an error message.
How to avoid this problem? there's my solution:
(max_file_size = 2,50 MB)
$fsize = $_FILES["userfile"]["size"];
if($fsize == 0 || $fsize > 2621000) exit("keep the filesize under 2,50MB!!");
When the size is bigger than the MAX_FILE_SIZE field, the value of $fsize is equal to 0 (zero) ......


michel s

I once had a problem with this function. File was uploaded correctly, but I still had to chmod the file afterwards. It could not be used otherwise.
Michel S


tnp

I may well also report this as a bug, but in the meantime I'll document what I have seen so far.
What I wanted to do, was take the uploaded temporary filename and pass it to MySql as part of a load_file  injection into a BLOB to store the file contents.
My first attempt was of an indexed array..
$filename=$_FILES[$index]["name"];
$filesize= $_FILES[$index]["size"];
$tmpname=$_FILES[$index]["tmp_name"];
followed by:-
$query=sprintf("insert into project_files set project_id='%s',current='yes',date='%s' ,user='%d', size='%d', description='%s', name='%s', content=LOAD_FILE('%s')",
    $project_id,
       date('Y-m-d'),
       $employee_id,
       $filesize,
       $filedescription,
       $filename,
       $tmpname);
mysql_query($query);
This gave me an empty content field. Zero bytes.
I took the echo $query string and applied it to MtySql interpreter. With a suitable filename it worked..well after fixing a few database perms..but no joy from PHP still.
I found this page and tried adding:-
move_uploaded_file($tmpname)"/tmp/foo");
$tmpname-"/tmp/foo";
before setting up the query.
That was worse. The browser just hung on me.
Then I tried
copy($tmpname,"/tmp/foo");
$tmpname="/tmp/foo";
That worked!?*&^
My CONJECTURE is that PHP has a meta file-system running, and the environment the program sees is NOT reflected into the OS (Debian etch, in this case) until it closes and flushes its buffers.
Copy() does in fact seem to result in a real disk write, but move_uploaded_file() does not, so when the name was passed to Mysqld, it didn't exist, or had zero content or maybe with a large file truncated content.
I can see the point of this: faster response for web browsers who get the program HTTP output over with before it settles down to its housekeeping, but in this case it's a bit of a disaster.
What is needed is a way to force the buffers to flush if that is what is going on...


masahigo

I found a great resource concerning uploads with PHP:
http://www.radinks.com/upload/config.php
They explain and tell how to optimize PHP installation to handle large file uploads. Helped me a lot!


guy

I could be wrong, but I usualy use
$uploadext = strtolower(strrchr($imagename,"."));
to find the file extension when uploading, as opposed to explode().


jessy dot diamondman

I am pretty new, and am having upload problems myself, but I think I can help out ffproberen2 at dodgeit dot com with his premission denied errors. I had these two, and I had to change the upload directory, not the tmp_upload_dir or what ever it is called. The move_uploaded_file meathod takes an upload location as the last parameter. I am running a bundled package of Apache, Php, mySQL and so on, and on mine, specifing a directory of '' will upload it into C:\Program Files\xampp\apache (my PC is my experimental server, I will get linux, but got to obtain it and internet cuts off after 196mb so can't download it) even though php file is in C:\Program Files\xampp\htdocs\xampp\jessyexum\upload_client.php.
This is a code that I found and then modified, hope it can help. It dosn't always upload every file type giving me an error #2.
<?php
$uploaddir = '';
$uploadfile = $uploaddir . basename($_FILES['upfile']['name']);
echo '<pre>';
if (move_uploaded_file($_FILES['upfile']['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>";
?>


mina86

Hey! Why not using strrchr() to get file  extension:
<?php $ext = strrchr($_FILES['file']['name'], '.'); ?>
or to get it without '.' at the begining:
<?php $ext = substr(strrchr($_FILES['file']['name'], '.'), 1); ?>
If you want to update file without any strang characters you can use:
<?php
move_uploaded_file(
 $_FILES["file"]["tmp_name"],
 $dir . preg_replace('/[^a-z0-9_\-\.]/i', '_', $_FILES["file"]["name"])
);
?>


nouncad

Great!! my first note here...
This function upload a file.
If file exist, create a copy as "filename[n].ext"
<?php
function subirFichero($origen, $destinoDir, $ftemporal) {
$origen = strtolower(basename($origen));
$destinoFull = $destinoDir.$origen;
$frand = $origen;
$i = 1;

while (file_exists( $destinoFull )) {
$file_name = substr($origen, 0, strlen($origen)-4);
$file_extension  = substr($origen, strlen($origen)-4, strlen($origen));
$frand = $file_name."[$i]".$file_extension;
$destinoFull = $destinoDir.$frand;
$i++;
}

if (move_uploaded_file($ftemporal, $destinoFull)) return $frand;
else return "0";
}
?>


user

Giving the directory 777 permission is not a good idea for security reasons, it would be better to create the directory using "mkdir()".
That will make php user (usually "nobody") the owner of the directory, and permissions will not be a problem.


www

function upload($filedir,$source,$source_name,$up_flag,$lastname)
{
if (!file_exists($filedir))
{
mkdir($filedir,0777);
}
@chmod($filedir,0777);
if (!$lastname)
{
$lastname=$source_name;
}
if (file_exists("$filedir/$lastname"))
{
if ($up_flag=="y")
{
@unlink($filedir/$lastname);
@move_uploaded_file($source,"$filedir/$lastname");
echo "$source_name OK
";
}
else
echo "$source_name ...
";
}
else
{
@move_uploaded_file($source,"$filedir/$lastname");
echo "$source_name OK
";
}
}


wolke74

French and English filenames --- as it is not forbidden -- often have an apostrophy, for instance "That's advertisement paper.doc" or "Les aventures d'Alice dans le pays du miracle.doc". However, uploading such files can run into trouble.
So you can write, if the posted file had been marked by myfile .
if(!move_uploaded_file($_FILES["myfile"]["tmp_name"],
rawurlencode($mydir.$_FILES["myfile"]["name"]))
{
    echo "Something is wrong with the file";
     exit;
}


bdeguire

For Windows users: Note that your file attributes from the temporary upload folder ("upload_tmp_dir" in php.ini) are copied to the destination folder (because the file is *moved*, not copied).
I ran into a problem when using Microsoft Indexing Server. For it to work, you must activate the attribute "For fast searching,  allow Indexing Server to index this folder". However, you must not only activate this attribute on the *final* destination folder, but ALSO on the temporary upload folder. So when the file is move from the temp location to the final location, the attributes are kept.


subway

Don't forget to set chmod to 777 for the directory to which you want to move the file.
Otherwise you will maybe get "failed to open stream: Permission denied in ..."!


richardno

Creating the dir with mkdir from php is a security risk too. Everyone who can run a php script on the server can write a script to mess with the dir.

dev

Can you explain more, how i can change the max memory size varible for the large file can work with move_uploaded_file()?
Thanks a lot!


rob szarka

Apparently the warning above might better be written "If the destination file already exists, it will be overwritten ... regardless of the destination file's permissions."
In other words, move_uploaded_file() executes as if it's root, not the user under which the web server is operating or the owner of the script that's executing.


sauron

An extension only does not really tell you what type of file it really is. I can easily rename a .jpg file to a .zip file and make the server think it is a ZIP file with webmaster kobrasrealm's code.
A better way is to use the Linux utility "file" to determine the file type. Although I'm aware that some users might use Windows on their webservers, I thought it's worth  mentioning the utility here. Using the backtick operators and preg_matches on the output, you can easily determine the file type safely, and fix the extension when necessary.


booc0mtaco

Also, make sure that the setting for the post_max_size allows for a proper file size range.
post_max_size = 128M ; Expands the size of POST data for file uploads


espiao

/**
* This function moves the archives and directoryes of a directory of
* origin for a directory destination being able replace them or not.
**/
function mvdir($oldDir, $newDir, $replaceFiles = true) {
if ($oldDir == $newDir) {
trigger_error("Destination directory is equal of origin.");
return false;
}

if (!($tmpDir = opendir($oldDir))) {
trigger_error("It was not possible to open origin directory.");
return false;
}
if (!is_dir($newDir)) {
trigger_error("It was not possible to open destination directory.");
return false;
}
while (($file = readdir($tmpDir)) !== false) {
if (($file != ".") && ($file !== "..")) {

$oldFileWithDir = $oldDir . $file;
$newFileWithDir = $newDir . $file;

if (is_dir($oldFileWithDir)) {

@mkdir($newFileWithDir."/", 0777);
@mvdir($oldFileWithDir."/", $newFileWithDir."/", $replaceFiles);
@rmdir($oldFileWithDir);
}
else {
if (file_exists($newFileWithDir)) {
if (!$replaceFiles) {

@unlink($oldFileWithDir);
continue;

}
}

@unlink($newFileWithDir);
@copy($oldFileWithDir, $newFileWithDir);
@chmod($newFileWithDir, 0777);
@unlink($oldFileWithDir);

}
}
}

return true;

}
/**
* This is an example of move with replace files on destination folder if
* exists files with the same names on destionatio folder
**/
mvdir("/var/www/example/", "/var/www/other_folder/");
/**
* This is an example of move without replace files on destination
* folder if  exists files with the same names on destionatio folder
**/
mvdir("/var/www/example/", "/var/www/other_folder/", false);


at-he at_he

---------
Note that post_max_size also needs to be considered, by default it is 8M. I raised my upload_max_filesize to 20M and was wondering why 10M uploads weren't working...
r: It could be because of your max execution time.
----------
try changing the value of both post_max_size and upload_max_filesize


Change Language


Follow Navioo On Twitter
basename
chgrp
chmod
chown
clearstatcache
copy
delete
dirname
disk_free_space
disk_total_space
diskfreespace
fclose
feof
fflush
fgetc
fgetcsv
fgets
fgetss
file_exists
file_get_contents
file_put_contents
file
fileatime
filectime
filegroup
fileinode
filemtime
fileowner
fileperms
filesize
filetype
flock
fnmatch
fopen
fpassthru
fputcsv
fputs
fread
fscanf
fseek
fstat
ftell
ftruncate
fwrite
glob
is_dir
is_executable
is_file
is_link
is_readable
is_uploaded_file
is_writable
is_writeable
lchgrp
lchown
link
linkinfo
lstat
mkdir
move_uploaded_file
parse_ini_file
pathinfo
pclose
popen
readfile
readlink
realpath
rename
rewind
rmdir
set_file_buffer
stat
symlink
tempnam
tmpfile
touch
umask
unlink
eXTReMe Tracker