is_uploaded_file

(PHP 4 >= 4.0.3, PHP 5, PHP 7, PHP 8)

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

说明

is_uploaded_file(string $filename): bool

如果名为 filename 的文件是通过 HTTP POST 上传的,则返回 true。这有助于确保恶意用户不会试图欺骗脚本处理不应该处理的文件,例如 /etc/passwd

如果存在任何可能性,即对上传文件执行的操作会将文件内容泄露给用户,甚至泄露给同一系统上的其他用户,则这种检查尤其重要。

为了正常工作,函数 is_uploaded_file() 需要一个类似于 $_FILES['userfile']['tmp_name'] 的参数,即客户端机器上上传文件的名称,$_FILES['userfile']['name'] 不起作用。

参数

filename

要检查的文件名。

返回值

成功时返回 true,失败时返回 false

示例

示例 #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'] . "'.";
}

?>

参见

添加注释

用户贡献的注释 14 个注释

62
nicoSWD
10 年前
请注意,在调用 move_uploaded_file() 之前调用此函数没有必要,因为它已经执行了完全相同的检查。它没有提供额外的安全性。只有当你尝试将上传的文件用于除了移动到新位置之外的用途时。

参考
https://github.com/php/php-src/blob/master/ext/standard/basic_functions.c#L5796
12
Robert Lerner
8 年前
为了扩展 nicoSWD 关于此函数的陈述。

任何使用临时文件 $_FILES[]['tmp_name'] 的脚本都应该调用此函数。

在任何情况下,如果脚本被修改为 unlink()、rename() 或以其他方式修改文件,而该修改并非 move_uploaded_file(),则不会检查上传。

同样,大多数文件操作在 PHP 中被缓存,因此在 move_uploaded_file 之前运行 is_uploaded_file 应该几乎没有性能损失,因为它通常会使用缓存的结果用于后者。

无论如何,安全优势超过了性能方面的微秒差异,并且应该在 $_FILES 数组第一次进入应用程序时普遍使用。虽然可能没有立即的问题,但代码会随着时间的推移而发展,并且很快就会改变这一事实。
18
info at metaltoad dot net
21 年前
从 PHP 4.2.0 开始,与其自动假设上传文件失败是文件攻击,你可以使用与文件上传关联的错误代码来检查并了解上传失败的原因。此错误代码存储在 userfile 数组中(例如:$HTTP_POST_FILES['userfile']['error'])。

以下是一个 switch 示例

if (is_uploaded_file($userfile)) {

// 在此处包含将临时文件复制到最终位置的代码...

}else{
switch($HTTP_POST_FILES['userfile']['error']){
case 0: // 无错误;可能的文件攻击!
echo "你的上传出现问题。";
break;
case 1: // 上传文件超过了 php.ini 中的 upload_max_filesize 指令
echo "你尝试上传的文件太大。";
break;
case 2: // 上传文件超过了 html 表单中指定的 MAX_FILE_SIZE 指令
echo "你尝试上传的文件太大。";
break;
case 3: // 上传文件仅部分上传
echo "你尝试上传的文件仅部分上传。";
break;
case 4: // 没有上传文件
echo "你必须选择一个图像进行上传。";
break;
default: // 默认错误,以防万一!:)
echo "你的上传出现问题。";
break;
}

此外,通过测试文件上传数组的 'name' 元素,你可以过滤掉不需要的文件类型(.exe、.zip、.bat 等)。以下是一个可以在测试文件是否上传之前添加的过滤器示例

// 拒绝所有 .exe、.com、.bat、.zip、.doc 和 .txt 文件
if(preg_match("/.exe$|.com$|.bat$|.zip$|.doc$|.txt$/i", $HTTP_POST_FILES['userfile']['name'])){
exit("你不能上传此类文件。");
}

// 如果文件未被过滤器拒绝,则正常继续
if (is_uploaded_file($userfile)) {

/* 剩余代码 */
4
uramihsayibok, gmail, com
17 年前
我在任何地方都没有看到过,但是 $filename 在 Windows 上确实区分大小写。
这意味着,虽然可能上传了 C:\Windows\TEMP\php123.tmp,但 C:\Windows\Temp\php123.tmp 却没有。

我发现这一点是因为我在文件名上使用了 realpath(),它“修复”了大小写(我的 Temp 文件夹是标题大小写,而不是大写 - 感谢 Vista)。

无论如何,问题是 PHP 使用 %TEMP% 来确定上传文件的目标位置,而 %TEMP% 使用了大写版本的路径。将其更改为使用标题大小写,然后重新启动 Apache 解决了问题。
4
YLearn
18 年前
刚才再次查看了我发布的内容,发现了一些重大和次要的错误。这就是我发帖前没有喝完咖啡的结果。这应该能更好地工作(即应该一开始就能工作)

<?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;
?>
3
匿名
10 年前
这是我的文件处理程序代码,希望对大家有所帮助

首先是一个处理文件上传的类
<?php
define
('UPLOAD_PATH', 'upload/');
define('MAXIMUM_FILESIZE', '10485760'); //10 MB
class FileHandler
{
private
$file_types = array('xls', 'xlsx');
private
$files = null;
private
$filename_sanitized = null;
private
$filename_original = null;


public function
__construct($files)
{
$this->files = $files;
}

public function
setFileTypes($fileTypes = array())
{
$this->file_types = $fileTypes;
return
$this;
}

public function
setFileNameOriginal($filename)
{
$this->filename_original = $filename;
}

public function
fileNameOriginal()
{
return
$this->filename_original;
}

public function
sanitize($cursor = 0)
{
$this->setFileNameOriginal($this->files['name'][$cursor]);

$safe_filename = preg_replace(
array(
"/\s+/", "/[^-\.\w]+/"),
array(
"_", ""),
trim($this->fileNameOriginal()));
$this->filename_sanitized = md5($safe_filename.time()).$safe_filename;
return
$this;
}

public function
fileSize($cursor = 0)
{
return
$this->files['size'][$cursor];
}

public function
extensionValid()
{
$fileTypes = implode('|', $this->file_types);
$rEFileTypes = "/^\.($fileTypes){1}$/i";
if(!
preg_match($rEFileTypes, strrchr($this->filename_sanitized, '.')))
throw new
Exception('未找到合适的文档类型');

return
$this;
}

public function
isUploadedFile($cursor)
{
if(!
is_uploaded_file($this->files['tmp_name'][$cursor]))
{
throw new
Exception("未获得文件上传");
}
}

public function
saveUploadedFile($cursor)
{
if(!
move_uploaded_file ($this->files['tmp_name'][$cursor],UPLOAD_PATH.$this->filename_sanitized))
throw new
Exception("未成功保存文件");
}

public function
fileNameSanitized()
{
return
$this->filename_sanitized;
}

public function
uploadFile($cursor = 0)
{
$this->isUploadedFile($cursor);
if (
$this->sanitize($cursor)->fileSize($cursor) <= MAXIMUM_FILESIZE)
{
$this->extensionValid()->saveUploadedFile($cursor);
}
else
{
throw new
Exception("文件过大.");
}
return
$name;
}

}

?>

接下来是使用该类的代码部分

<?php
//表单提交并检测到
if ($form_submited == 1)
{
try
{
//我假设输入文件是:
/*
<input id="<?php echo EXCEL_FILE;?>[]" name="<?php echo EXCEL_FILE;?>[]" type="file"/>
其中 EXCEL_FILE 是常量:
define('EXCEL_FILE', 'excel_file');
*/
$file = new FileHandler($_FILES['excel_file']);
$inputFileName = $file->uploadFile()->fileNameSanitized(); // 要读取的文件
...

}
catch(
Exception $e)
{
die(
'上传文件 "'.($file->fileNameOriginal()).'": '.$e->getMessage());
}


}
?>
2
itadmin at itmusicweb dot co dot uk
21 年前
给出的示例无法按预期工作

function is_uploaded_file($filename) {
if (!$tmp_file = get_cfg_var('upload_tmp_dir')) {
$tmp_file = dirname(tempnam('', ''));
}
$tmp_file .= '/' . basename($filename);
/* 用户可能在 php.ini 中有尾部斜杠... */
return (ereg_replace('/+', '/', $tmp_file) == $filename);
}

它只适用于小于 ....4 或 5 kb 的文件,其他文件会自动变成 0 字节大小。所以这里肯定有问题。内置的 is_uploaded_file() 工作正常。
1
juk
18 年前
如果你的 $_FILES 和 $_POST 为空,这可能是由于
- php.ini 中 post_max_size 设置的限制
- php.ini 中 upload_max_filesize 设置的限制

不幸的是,第一个限制不会在 $_FILES['error'] 中报告为错误代码。
0
匿名
19 年前
确保你在表单标签中使用了 enctype="multipart/form-data",否则什么也行不通... 我花了两个小时才找到这个原因.......
0
troels at NO dot SPAM dot webcode dot dk
21 年前
要在 Windows 上让示例工作,你必须添加一行代码,将反斜杠替换为斜杠。例如:$filename = str_replace ("\\", "/", $filename);

此外,正如有人提到的,全局化 $HTTP_POST_FILES 是个好主意...

<pre>
/* 用户空间上传文件测试。 */
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);
/* 用户可能在 php.ini 中有尾部斜杠... */
// 针对 win 平台的修复
$filename = str_replace ("\\", "/", $filename);
return (ereg_replace("/+", "/", $tmp_file) == $filename);
}
</pre>
-1
lots2learn at gmail dot com
19 年前
如果文件没有上传,并且 $_FILE 数组为空,而你的代码看起来没问题,那么检查 php.ini 文件。file_uploads 选项应该设置为 'On' 以允许文件上传。打开它并重启 Apache 以使更改生效。
-2
beer UNDRSCR nomaed AT hotmail DOT com
19 年前
关于 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...
?>

这行不通。它会运行,但不会运行正确。
你应该为 preg 函数转义 .(点),
并为 PHP 转义 $(美元)符号,或者使用
单引号字符串...

语法应该是(更短更简洁)

<?php
// ... yada yada yada...
preg_match('/\\.(exe|com|bat|zip|doc|txt)$/i', $_FILES['userfile']['name']))
// ... yada yada yada...
?>
-2
YLearn
18 年前
关于 topcat 建议的更改,我对此持保留意见。我不喜欢向用户显示可能会提供过多信息的错误(或者表明我没有针对特定错误提供解决方案)。但我想知道何时出现落入默认情况的错误,以便我可以修复我的代码。我通常会将它们写入错误日志,类似于对 metaltoad 帖子进行如下修改(考虑了 error_log 无法处理的可能的多行错误)

<?php
default: //默认错误,以防万一!:)
echo "上传文件时出现问题。";
$err_msg = "无法识别的文件 POST 错误: ".$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;
?>
-11
topcat
19 年前
关于 metaltoad 评论中的信息,提供一个小建议
当无法识别错误代码时,打印错误代码是一种良好的做法

default: //打印错误代码
echo "无法识别的错误代码: ".$HTTP_POST_FILES['userfile']['error'];
break;
To Top