PHP Conference Japan 2024

move_uploaded_file

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

move_uploaded_file将上传的文件移动到新位置

描述

move_uploaded_file(字符串 $from, 字符串 $to): 布尔值

此函数检查以确保由 from 指定的文件是有效的上传文件(这意味着它是通过 PHP 的 HTTP POST 上传机制上传的)。如果文件有效,它将被移动到 to 给出的文件名。

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

参数

from

上传文件的名称。

to

移动文件的目标位置。

返回值

成功时返回 true

如果 from 不是有效的上传文件,则不会执行任何操作,并且 move_uploaded_file() 将返回 false

如果 from 是有效的上传文件,但由于某种原因无法移动,则不会执行任何操作,并且 move_uploaded_file() 将返回 false。此外,还会发出警告。

示例

示例 #1 上传多个文件

<?php
$uploads_dir
= '/uploads';
foreach (
$_FILES["pictures"]["error"] as $key => $error) {
if (
$error == UPLOAD_ERR_OK) {
$tmp_name = $_FILES["pictures"]["tmp_name"][$key];
// basename() 可以防止文件系统遍历攻击;
// 可能需要进一步验证/清理文件名
$name = basename($_FILES["pictures"]["name"][$key]);
move_uploaded_file($tmp_name, "$uploads_dir/$name");
}
}
?>

注释

注意:

move_uploaded_file() 能够识别 open_basedir。但是,仅对 to 路径设置限制,以便可以移动上传的文件,其中 from 可能与这些限制冲突。move_uploaded_file() 通过仅允许移动通过 PHP 上传的文件来确保此操作的安全。

警告

如果目标文件已存在,它将被覆盖。

参见

添加注释

用户贡献的注释 13 条注释

213
Yousef Ismaeil Cliprz
11 年前
使用此函数之前必须知道的安全提示

首先:确保文件不为空。

其次:确保文件名使用英文字符、数字和(_-。)符号,以提高安全性。

您可以在示例中使用以下函数

<?php

/**
* 检查 $_FILES[][name]
*
* @param (字符串) $filename - 上传的文件名。
* @author Yousef Ismaeil Cliprz
*/
function check_file_uploaded_name ($filename)
{
(布尔值) ((
preg_match("`^[-0-9A-Z_\.]+$`i",$filename)) ? true : false);
}

?>

第三:确保文件名不超过 250 个字符。

例如

<?php

/**
* 检查 $_FILES[][name] 的长度。
*
* @param (字符串) $filename - 上传的文件名。
* @author Yousef Ismaeil Cliprz。
*/
function check_file_uploaded_length ($filename)
{
return (布尔值) ((
mb_strlen($filename,"UTF-8") > 225) ? true : false);
}

?>

第四:检查您希望在项目中允许的文件扩展名和 MIME 类型。您可以使用:pathinfo() https://php.net/pathinfo

或者您可以使用正则表达式来检查文件扩展名,例如

#^(gif|jpg|jpeg|jpe|png)$#i

或使用 in_array 检查,例如

<?php

$ext_type
= array('gif','jpg','jpe','jpeg','png');

?>

您有多种选择来检查扩展名和 MIME 类型。

第五:检查文件大小,并确保 php.ini 中上传文件的大小限制符合您的预期,您可以从 https://php.net/manual/en/ini.core.php#ini.file-uploads 开始

最后但并非最不重要的一点:检查文件内容是否包含不良代码或类似内容,例如 https://php.net/manual/en/function.file-get-contents.php. 中的函数。

您可以使用 .htaccess 来停止某些脚本在上传路径中的 php 文件中运行。

使用

AddHandler cgi-script .php .pl .jsp .asp .sh .cgi
Options -ExecCGI

不要忘记为您的项目保护执行这些步骤。
104
matthias dot dailey at gmail dot com
13 年前
目标目录必须存在;move_uploaded_file() 不会自动为您创建它。
4
adeel dot cs at gmail dot com
9 个月前
权限问题。

如果您在文件夹上设置了 setgid 即 g+s,并且想知道为什么创建的文件属于 www-data:www-data,请注意上传的文件首先保存在 /tmp 文件夹中,并由 Web 用户拥有。



move_uploaded_file() 命令将文件从 /tmp 移动到给定的 TO 目录,包括 /temp 文件当前的权限。

因此,setgid 被忽略,并且不会继承父目录的权限。
31
Dan Delaney
16 年前
对于在 Windows 和 IIS 上使用 PHP 的用户,您应该在 php.ini 中将 "upload_tmp_dir" 值设置为网站目录附近的一些目录,创建该目录,然后为其设置与网站目录相同的权限。否则,当您上传文件并将其放入 C:\WINDOWS\Temp 时,然后将其移动到您的网站目录,其权限将不会正确设置。如果您随后想使用 ImageMagick 的 convert 实用程序之类的工具来操作该文件,这将导致问题。
8
shacker at birdhouse dot org
18 年前
如果您正在处理通过某些外部 FTP 源上传的文件,并且需要将其移动到最终目的地,则搜索 php.net 上的“mv”或“move”将无法获得您想要的结果。您需要 rename() 函数。

https://php.net/manual/en/function.rename.php

(move_uploaded_file() 将不起作用,因为 POST 变量将不存在。)
6
Zarel
18 年前
nouncad at mayetlite dot com 发布了一个上传文件的函数,如果文件已经存在,则会将其重命名为 filename[n].ext

它只适用于扩展名正好为三个字母的文件,所以我修复了它(并在修复过程中进行了一些其他改进)。

<?php
// 用法: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;
}
?>
1
Juliano P. Santos
5 年前
对于那些将使用 inotify-tools 在 move_uploaded_file 将文件放入特定目录时启动事件的用户,请注意,move_uploaded_file 将触发创建事件,而不是 inotify-tools 的移动事件。
3
Florian S. in H. an der E. [.de]
16 年前
move_uploaded_file(在我的设置中)始终使文件为 0600(“rw- --- ---”)并且归运行 Web 服务器的用户所有(所有者和组)。
即使目录已将粘滞位设置为组权限!
我找不到任何通过 php.ini 或甚至使用“umask()”更改此设置的方法。

我希望服务器上的普通用户能够“tar cjf”目录……这将在完全由 Web 服务器进程用户拥有的文件上失败;
但是,“copy(from, to)”函数会遵守粘滞位!
0
chelidze dot givia at gmail dot com
1 年前
使用 move_uploaded_file() 时。如果用户上传的文件名已存在,move_uploaded_file() 将覆盖它。最佳实践是在创建您的卡片/用户/产品等时生成的目录中存储图像……

<?php
function generateDir(int $n): string {
$characters="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
$dir = "";
for(
$i = 0; $i<$n; $i++){
$index = rand(0, strlen($characters)-1);
$dir .= $characters[$index];
}
return
$dir;
}

$image = $_FILES["image"];
$imagePath = 'images/'. generateDir(10) .'/'. $image["name"];

// 首先创建目录,否则上传将不会继续。
mkdir($imagePath);

// 一些错误处理等...

move_uploaded_file($image["tmp_name"], $imagePath);
?>
0
benbrown3882 at gmail dot com
1 年前
确保上传临时目录和目标目录对“其他”用户具有“写入”权限。
0
nlgordon at iastate dot edu
17 年前
只是一个有用的注释。如果您设置了 open_basedir,则必须将 upload_tmp_dir 设置为 open_basedir 内的某个位置。否则,文件上传将被拒绝。move_uploaded_file 可能知道 open_basedir,但上传过程的其余部分则不知道。
-1
jest3r at mtonic dot net
19 年前
似乎 move_uploaded_file 使用 tmp 文件位置的父目录的 GROUP 权限,而简单的“copy”则使用 apache 进程的组。如果您的 tmp 文件位置由 root:wheel 拥有,这可能会造成安全噩梦。
-4
Rob Szarka
18 年前
显然,上面的警告最好写成“如果目标文件已存在,它将被覆盖……无论目标文件的权限如何”。

换句话说,move_uploaded_file() 的执行方式就像它是 root 一样,而不是 Web 服务器正在运行的用户或正在执行脚本的所有者。
To Top