move_uploaded_file

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

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

描述

move_uploaded_file(string $from, string $to): bool

此函数检查以确保由 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 上传的文件被移动来确保此操作的安全性。

警告

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

参见

添加注释

用户贡献注释 20 个注释

Yousef Ismaeil Cliprz
11 年前
使用此函数之前您必须了解的安全提示

首先:确保文件不为空。

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

您可以像示例中一样使用以下函数

<?php

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

?>

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

例如

<?php

/**
* 检查 $_FILES[][name] 的长度。
*
* @param (string) $filename - 上传的文件名。
* @author Yousef Ismaeil Cliprz.
*/
function check_file_uploaded_length ($filename)
{
return (bool) ((
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

不要忘记为您的项目保护执行这些步骤。
adeel dot cs at gmail dot com
5 个月前
权限问题。

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

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

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

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

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

我希望服务器上的普通用户能够“tar cjf”该目录.. 这将在完全由 web 服务器进程用户拥有的文件上失败;
不过,“copy(from, to)”函数会遵守粘性位!
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);
?>
benbrown3882 at gmail dot com
1 年前
确保上传临时目录和目标目录对其他用户具有“写入”权限。
nlgordon at iastate dot edu
17 年前
只是一个有用的评论。如果您已设置 open_basedir,那么您必须将 upload_tmp_dir 设置为 open_basedir 内部的某个位置。否则将拒绝文件上传。move_uploaded_file 可能支持 open_basedir,但上传过程的其余部分则不支持。
jest3r at mtonic dot net
18 年前
move_uploaded_file 使用 tmp 文件位置的父目录的组权限,而简单的“copy”则使用 apache 进程的组。如果您的 tmp 文件位置归 root:wheel 所有,这可能会造成安全噩梦。
Rob Szarka
17 年前
显然,上面的警告可能最好写成“如果目标文件已经存在,它将被覆盖…无论目标文件的权限如何”。

换句话说,move_uploaded_file() 的执行方式就好像它是 root,而不是运行 web 服务器的用户或正在执行脚本的用户。
bogusred
17 年前
如果您在 *nix 环境中有一个目录,用于存储所有文件上传,并且您的 php 脚本只有在该目录的权限设置为 777 时才能正常工作,那么您可以通过以下方法修复它,以便您在仍然允许 php 脚本工作(包括 move_uploaded_file())的同时获得 755 的安全性优势。

通过 shell 访问,导航到包含您的上传文件夹的目录,然后运行以下 2 个命令
chown -R nobody uploaddir
chmod -R 755 uploaddir

将“uploaddir”替换为您的上传目录的名称。第一个命令将目录和文件的拥有者更改为“nobody”,这就是 php 在其下运行的。第二个命令将文件夹和文件更改为仅允许用户访问写入。这更安全。

希望这能帮助那些遇到和我一样问题的其他人。
Tom
9 年前
它没有说明如何在失败时获取错误/警告消息。

我所知道的唯一方法是这样的

if (move_uploaded_file($_FILES["file1"]["tmp_name"], $target_file)) {
echo "<P>FILE UPLOADED TO: $target_file</P>";
} else {
echo "<P>MOVE UPLOADED FILE FAILED!</P>";
print_r(error_get_last());
}
Mark Kazemier
15 年前
我和下面两个评论中的人遇到了同样的问题。当我使用 move_uploaded_file 函数时,文件的权限设置为 0600。无论您设置什么配置。

我在网上搜索,发现更多人遇到相同的问题,但没有解决方案。我将 apache 的 umask 设置为 013,但文件仍然设置为 0600。

copy 函数解决了这个问题。另一种解决此问题的办法是在上传后使用 chmod 函数。
labsy at seznam dot org
13 年前
关于 PHP 在 Windows IIS 平台上的说明
PHP 显然不喜欢跨分区进行目录遍历,因此,如果您将 upload_tmp_dir 设置为与 php-cgi.exe 或 php.exe 位于不同分区,则 upload_tmp_dir 将无法用于文件上传!任何尝试上传文件的操作都将出现 ERROR 6,文件大小将为 0。
解决方法是将 upload_tmp_dir 设置为 PHP 安装文件夹下的路径。
…并且确保该文件夹(以及 session_save_path 文件夹)至少具有授予 AppPool 拥有者(通常是 NETWORK SERVICE)和 IIS web 用户(默认情况下是 IUSR_)的读写权限。
wilcobeekhuizen at gmail dot com
13 年前
在上传文件名非常长,例如 255 个字符的文件时,move_uploaded_file 会失败。我成功上传的最长文件的文件名为 247 个字符。因此,虽然您可以在本地创建 250 个字符的文件名,但服务器可能无法移动它。
Ray.Paseur sometimes uses Gmail
13 年前
您只能移动一次上传的文件。如果您需要将文件放在多个位置,可以使用 copy()。
<?php // RAY_temp_upload_example.php
error_reporting(E_ALL);
echo
"<pre>" . PHP_EOL;

// 如果已上传文件
if (!empty($_FILES))
{
// 显示已上传文件
print_r($_FILES);

// 尝试两次移动文件 - 第二次移动返回 FALSE
if (!move_uploaded_file($_FILES["userfile"]["tmp_name"], $_FILES["userfile"]["name"])) echo "CANNOT MOVE {$_FILES["userfile"]["name"]}" . PHP_EOL;
if (!
move_uploaded_file($_FILES["userfile"]["tmp_name"], $_FILES["userfile"]["name"])) echo "CANNOT MOVE {$_FILES["userfile"]["name"]}" . PHP_EOL;

// 移动后显示已上传文件 - 无明显变化
print_r($_FILES);
}

// PHP 代码结束,放入获取文件的 HTML 表单
?>
<!-- 数据编码类型 enctype 必须指定为如下所示 -->
<form enctype="multipart/form-data" method="POST">
<!-- MAX_FILE_SIZE 必须放在文件输入字段之前 -->
<input type="hidden" name="MAX_FILE_SIZE" value="300000" />
<!-- 输入元素名称决定 $_FILES 数组中的名称 -->
发送此文件:<input name="userfile" type="file" />
<input type="submit" value="发送文件" />
</form>
chatchaw at tot dot co dot th
14 年前
当您使用 move_uploaded_file 函数将带有 utf-8 文件名的文件上传到 Linux 系统时,您可能通过浏览目标目录查看文件来检查结果,因此请确保您的终端模拟器或 Samba 配置已将字符编码设置为 utf-8,否则您的文件将显示为 ??????(不可读字符)。
To Top