Filesystem 函数
在线手册:中文 英文
PHP手册

is_uploaded_file

(PHP 4 >= 4.0.3, PHP 5)

is_uploaded_file判断文件是否是通过 HTTP POST 上传的

说明

bool is_uploaded_file ( string $filename )

如果 filename 所给出的文件是通过 HTTP POST 上传的则返回 TRUE。这可以用来确保恶意的用户无法欺骗脚本去访问本不能访问的文件,例如 /etc/passwd

这种检查显得格外重要,如果上传的文件有可能会造成对用户或本系统的其他用户显示其内容的话。

为了能使 is_uploaded_file() 函数正常工作,必段指定类似于 $_FILES['userfile']['tmp_name'] 的变量,而在从客户端上传的文件名 $_FILES['userfile']['name'] 不能正常运作。

Example #1 is_uploaded_file() 例子

<?php

if (is_uploaded_file($_FILES['userfile']['tmp_name'])) {
   echo 
"File "$_FILES['userfile']['name'] ." uploaded successfully.\n";
   echo 
"Displaying contents\n";
   
readfile($_FILES['userfile']['tmp_name']);
} else {
   echo 
"Possible file upload attack: ";
   echo 
"filename '"$_FILES['userfile']['tmp_name'] . "'.";
}

?>

is_uploaded_file() 仅可用于 PHP 3 的 3.0.16 版之后,以及 PHP 4 的 4.0.2 版之后。如果执意要用老版本,可以用下面的函数来保护自己:

Note:

以下例子不能用于 PHP 4 的 4.0.2 版之后。它依赖的 PHP 内部函数在该版本之后改变了。

Example #2 is_uploaded_file() 可运行于 PHP 4 < 4.0.3 的例子

<?php
/* Userland test for uploaded file. */
function is_uploaded_file_4_0_2($filename)
{
    if (!
$tmp_file get_cfg_var('upload_tmp_dir')) {
        
$tmp_file dirname(tempnam(''''));
    }
    
$tmp_file .= '/' basename($filename);
    
/* User might have trailing slash in php.ini... */
    
return (ereg_replace('/+''/'$tmp_file) == $filename);
}

/* This is how to use it, since you also don't have
 * move_uploaded_file() in these older versions: */
if (is_uploaded_file_4_0_2($HTTP_POST_FILES['userfile'])) {
    
copy($HTTP_POST_FILES['userfile'], "/place/to/put/uploaded/file");
} else {
    echo 
"Possible file upload attack: filename '$HTTP_POST_FILES[userfile]'.";
}
?>

参见 move_uploaded_file(),以及文件上传处理一章中的简单使用例子。


Filesystem 函数
在线手册:中文 英文
PHP手册
PHP手册 - N: 判断文件是否是通过 HTTP POST 上传的

用户评论:

Anonymous (02-Mar-2008 11:57)

> is_uploaded_file($_FILES['userfile']['tmp_name']);
> MUST always evaluate to true by definition which is, of course, useless.

This is not the case if there was an error in transmission (i.e. upload_max_filesize exceeded), because then tmp_name contains an empty string.
So, this function still has the obvious use of an is_file() call, even for a trusted argument and does NOT ALWAYS evaluate to true.

exonie at mail dot notpartofaddress dot bg (17-Sep-2007 11:37)

The description in the manual above says that the function needs an argument like:
<?php
$_FILES
['userfile']['tmp_name']
?>
Which is incorrect! This variable returns the pathname of the temp file (with random filename) that is created by php and, besides, is a superglobal so there is no way that it is overridden to point to another file (by injection when register_globals is on for example). Which means that:
<?php
is_uploaded_file
($_FILES['userfile']['tmp_name']);
?>
MUST always evaluate to true by definition which is, of course, useless.

The function is to be used only with variables that are prone to injection, which should not happen anyway since everyone should have their register globals off (finally we get rid of that in PHP6).

Only $_FILES['userfile']['name'] contains user submitted string and should be sanitized if it's to be used as a filename because it could (could it?) contain relative or absolute path or undesirable filename. The former is done simply with:
<?php
  $filename
= basename($_FILES['userfile']['name']);
?>

uramihsayibok, gmail, com (13-Aug-2007 05:18)

It isn't mentioned anywhere that I've seen, but $filename *is* case-sensitive on Windows.
It means that while C:\Windows\TEMP\php123.tmp may have been uploaded, C:\Windows\Temp\php123.tmp was not.

I found this out because I was using realpath() on the filename which 'fixed' the case (my Temp folder is in titlecase, not uppercase - thank you Vista).

Anyways, the problem was that PHP used %TEMP% to determine the destination for the uploaded file, and %TEMP% used the all-capitals version of the path. Changing it to use titlecase instead + restarting Apache fixed the problem.

tejas at kaushalam dot com (29-Mar-2006 07:28)

<FORM ENCTYPE="multipart/form-data" ACTION="_URL_" METHOD=POST>
Upload this file: <INPUT NAME="userfile" TYPE="file">
<INPUT TYPE="submit" VALUE="Send File"></FORM>

This html code will display a text area with file upload button. The form tag is bit different than the normal form tag used ( see the encrypte =).

Now let us go to the php part to handle the uploaded file. We have In PHP 3, the following variables will be defined within the destination script upon a successful upload, assuming that register_globals is turned on in php3.ini. If track_vars is turned on, they will also be available in PHP 3 within the global

array $HTTP_POST_VARS. Note that the following variable names assume the use of the file upload name 'userfile', as used in the example above:

    * $userfile - The temporary filename in which the uploaded file was stored on the server machine.
    * $userfile_name - The original name or path of the file on the sender's system.
    * $userfile_size - The size of the uploaded file in bytes.
    * $userfile_type - The mime type of the file if the browser provided this information. An example would be "image/gif".

With all these info we will make our script ready to handle the files.
Let us check the file size and we will not allow file size more than 250 KB to get uploaded to our server. Here we are using a flag $file_upload to false for processing the file upload.

if ($userfile_size >250000){$msg=$msg."Your uploaded file size is more than 250KB so please reduce the file size and then upload. Visit the help page to know how to reduce the file size.<BR>";
$file_upload="false";}

Now let us check the file extension and only jpg or gif file pictures we will allow into our server. We will check this by using $userfile_type extension

if (!($userfile_type =="image/pjpeg" OR $userfile_type=="image/gif")){$msg=$msg."Your uploaded file must be of JPG or GIF. Other file types are not allowed<BR>";
$file_upload="false";}
We will limit ourselves to these two type checks and if we find our $file_upload variable is not "false" then we can upload the file. This part is very simple in PHP and can be done in one step. Before that let us decide the file directory name where we will be placing the file. $add is the path with the file name relative to the script running this code where the uploaded file will be stored.

$add="upload/$userfile_name"; // the path with the file name where the file will be stored, upload is the directory name.

Now let us copy the file to server. The command move_uploaded_file will does that job for us and if the action is successful then it will return true.

if(move_uploaded_file ($userfile, $add)){
// do your coding here to give a thanks message or any other thing.
}else{echo "Failed to upload file Contact Site admin to fix the problem";}
Thats all... the file is placed in our directory (name: upload)

YLearn (10-Oct-2005 06:42)

Just looked at what I posted again and found several mistakes of the major and minor sort.  That's what I get for posting before I finish my coffee.  This should work better (i.e. should work in the first place):

<?php
  
default: //a default error, just in case!  :)
      
echo "There was a problem with your upload.";
      
$err_msg = "Unrecognized file POST error: ".$HTTP_POST_FILES['userfile']['error'];
       if (!(
strpos($err_msg, "\n") === false)) {
          
$err_lines = explode("\n", $err_msg);
           foreach (
$err_lines as $msg) {
              
error_log($msg, 0);
           }
       } else {
          
error_log($err_msg, 0);
       }
       break;
?>

YLearn (10-Oct-2005 05:41)

Regarding topcat's suggested change, I am split on doing that.  I don't like showing users errors that may give them more information than they should have (or show that I haven't provided for that particular error).  But I want to know when there are errors that fall to the default case so I can fix my code.  What I will typically do is write them to the error log something like this modification to metaltoad's post (takes into account the possibility of multi-line errors which error_log doesn't handle well):

<?php
   
default: //a default error, just in case!  :)
       
echo "There was a problem with your upload.";
       
$err_msg = "Unrecognized file POST error: ".$HTTP_POST_FILES['userfile']['error'];
        if ((
strpos($err_msg, "\n") === 0) {
           
$err_lines = explode("\n", $err_msg);
            foreach (
$err_lines as $msg) {
               
error_log($msg, 0);
            }
        } else {
           
error_log($err_msg, 0)
        }
        break;
?>

juk (18-Sep-2005 11:26)

If your $_FILES and $_POST are empty, this can be due to
- the limit set by post_max_size in php.ini
- the limit set by upload_max_filesize in php.ini

Unfortunately the first limit is not reported back as an error code in $_FILES['error'].

topcat (14-Jul-2005 12:56)

Just a little tip to info at metaltoad's comment:
It's good practice to print error code when it can't be recognized:

   default: //print the error code
     echo "Unrecognized error code: ".$HTTP_POST_FILES['userfile']['error'];
     break;

(23-Apr-2005 08:29)

make use u got the enctype="multipart/form-data" in ur form tag otrherwise nothing works... took me two hours to find that out.......

beer UNDRSCR nomaed AT hotmail DOT com (15-Apr-2005 12:21)

Regarding the comment of info at metaltoad dot net
@ 19-Feb-2003 04:03

<?php
// ... yada yada yada...
preg_match("/.exe$|.com$|.bat$|.zip$|.doc$|.txt$/i", $HTTP_POST_FILES['userfile']['name']))
// ... yada yada yada...
?>

This will not work. It will, but not correctly.
You shuld escape the . (dot) for the preg function,
and escape the $ (dollar) sign for PHP, or use
single-quoted string...

The syntax should be (much shorter and neater):

<?php
// ... yada yada yada...
preg_match('/\\.(exe|com|bat|zip|doc|txt)$/i', $_FILES['userfile']['name']))
// ... yada yada yada...
?>

lots2learn at gmail dot com (07-Feb-2005 05:13)

if files are not getting uploaded and $_FILE array is empty ..and your code looks fine..then check php.ini file..the file_uploads option should be turned 'On' to allow file uploads. Turn it on and restart apache to have effect .

Gordon Luk (04-Oct-2004 11:55)

If the $_FILES array suddenly goes mysteriously empty, even though your form seems correct, you should check the disk space available for your temporary folder partition. In my installation, all file uploads failed without warning. After much gnashing of teeth, I tried freeing up additional space, after which file uploads suddenly worked again.

vbudov_yahoo.com (22-May-2004 12:58)

Before moving the file in to place it's also a good idea to check if file with the same name already exists on the server.
If file exists then create unique name for the new file.

$num=1;
while (file_exists($destination)){    
    $num++; // if previous file name existed then thy another number+_+filename                                                                                                      
    $file_name = $num."_".$_FILES['userfile']['name'];
    $destination = $uploadpath.$file_name;
}                                                                                                                                  
move_uploaded_file( $source, $destination );

phpnetmark at nunswithguns dot co dot uk (16-Apr-2003 06:53)

If you are importing the uploaded file into a BLOB field in a mysql database and you are using LOAD_FILE() sql statement then be aware that  mysql checks max-allowed-packet mysql variable.

- if the size of the binary file LOAD_FILE is importing is bigger than
max-allowed-packet size then it LOAD_FILE will return null.

You can specify max-allowed-packet size in the call to mysqld eg:

./bin/safe_mysqld --user=mysql --max-allowed-packet=16M &

Full info on this variable is available here:
http://www.mysql.com/doc/en/Packet_too_large.html

info at metaltoad dot net (19-Feb-2003 09:03)

As of PHP 4.2.0, rather than automatically assuming a failed file uploaded is a file attack, you can use the error code associated with the file upload to check and see why the upload failed.  This error code is stored in the userfile array (ex: $HTTP_POST_FILES['userfile']['error']).

Here's an example of a switch:

if (is_uploaded_file($userfile)) {
 
  //include code to copy tmp file to final location here...
 
}else{
  switch($HTTP_POST_FILES['userfile']['error']){
    case 0: //no error; possible file attack!
      echo "There was a problem with your upload.";
      break;
    case 1: //uploaded file exceeds the upload_max_filesize directive in php.ini
      echo "The file you are trying to upload is too big.";
      break;
    case 2: //uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the html form
      echo "The file you are trying to upload is too big.";
      break;
    case 3: //uploaded file was only partially uploaded
      echo "The file you are trying upload was only partially uploaded.";
      break;
    case 4: //no file was uploaded
      echo "You must select an image for upload.";
      break;
    default: //a default error, just in case!  :)
      echo "There was a problem with your upload.";
      break;
}

Additionally, by testing the 'name' element of the file upload array, you can filter out unwanted file types (.exe, .zip, .bat, etc).  Here's an example of a filter that can be added before testing to see if the file was uploaded:

//rejects all .exe, .com, .bat, .zip, .doc and .txt files
if(preg_match("/.exe$|.com$|.bat$|.zip$|.doc$|.txt$/i", $HTTP_POST_FILES['userfile']['name'])){
  exit("You cannot upload this type of file.");
}

//if file is not rejected by the filter, continue normally
if (is_uploaded_file($userfile)) {

/*rest of code*/

itadmin at itmusicweb dot co dot uk (28-Nov-2002 02:11)

The example brought out does not work as supposed to:

function is_uploaded_file($filename) {
    if (!$tmp_file = get_cfg_var('upload_tmp_dir')) {
        $tmp_file = dirname(tempnam('', ''));
    }
    $tmp_file .= '/' . basename($filename);
    /* User might have trailing slash in php.ini... */
    return (ereg_replace('/+', '/', $tmp_file) == $filename);
}

It works only with files under ....4 or 5 kb, other files automatically get the size of 0 bytes. So something must be wrong here. Built-in is_uploaded_file() works good.

troels at NO dot SPAM dot webcode dot dk (14-Oct-2002 08:44)

to get the example to work on windows, youll have to add a line, that replaces backslashes with slashes. eg.: $filename = str_replace ("\\", "/", $filename);

also, as someone mentioned, globalizing $HTTP_POST_FILES is a good idea ...

<pre>
/* Userland test for uploaded file. */
function is_uploaded_file($filename)
{
    global $HTTP_POST_FILES;
    if (!$tmp_file = get_cfg_var("upload_tmp_dir")) {
        $tmp_file = dirname(tempnam("", ""));
    }
    $tmp_file .= "/" . basename($filename);
    /* User might have trailing slash in php.ini... */
    // fix for win platform
    $filename = str_replace ("\\", "/", $filename);
    return (ereg_replace("/+", "/", $tmp_file) == $filename);
}
</pre>

r3gan at hotmail dot com (17-Jun-2002 08:24)

Remeber, if using $HTTP_POST_FILES inside a function and it doesn't seem to work, try globalizing the array:

function UploadFile() {

    global $HTTP_POST_FILES;

    // rest of your code here

}  // end UploadFile