PHP 日本会议 2024
添加注释

用户贡献的注释 29 条注释

CertaiN
10 年前
您最好彻底检查 $_FILES 结构和值。
以下代码绝对不会导致任何错误。

示例
<?php

header
('Content-Type: text/plain; charset=utf-8');

try {

// 未定义 | 多个文件 | $_FILES 损坏攻击
// 如果此请求属于任何一种情况,则将其视为无效。
if (
!isset(
$_FILES['upfile']['error']) ||
is_array($_FILES['upfile']['error'])
) {
throw new
RuntimeException('无效参数。');
}

// 检查 $_FILES['upfile']['error'] 值。
switch ($_FILES['upfile']['error']) {
case
UPLOAD_ERR_OK:
break;
case
UPLOAD_ERR_NO_FILE:
throw new
RuntimeException('未发送文件。');
case
UPLOAD_ERR_INI_SIZE:
case
UPLOAD_ERR_FORM_SIZE:
throw new
RuntimeException('超过文件大小限制。');
default:
throw new
RuntimeException('未知错误。');
}

// 您也应该在此处检查文件大小。
if ($_FILES['upfile']['size'] > 1000000) {
throw new
RuntimeException('超过文件大小限制。');
}

// 不要相信 $_FILES['upfile']['mime'] 值!!
// 自己检查 MIME 类型。
$finfo = new finfo(FILEINFO_MIME_TYPE);
if (
false === $ext = array_search(
$finfo->file($_FILES['upfile']['tmp_name']),
array(
'jpg' => 'image/jpeg',
'png' => 'image/png',
'gif' => 'image/gif',
),
true
)) {
throw new
RuntimeException('无效的文件格式。');
}

// 您应该为其命名唯一。
// 不要在没有验证的情况下使用 $_FILES['upfile']['name']!!
// 在此示例中,从其二进制数据中获取安全的唯一名称。
if (!move_uploaded_file(
$_FILES['upfile']['tmp_name'],
sprintf('./uploads/%s.%s',
sha1_file($_FILES['upfile']['tmp_name']),
$ext
)
)) {
throw new
RuntimeException('移动上传文件失败。');
}

echo
'文件上传成功。';

} catch (
RuntimeException $e) {

echo
$e->getMessage();

}

?>
steve dot criddle at crd-sector dot com
20 年前
Mac 上的 IE 有点麻烦。如果您正在上传一个具有未知文件后缀的文件,IE 会使用“application/x-macbinary”的 MIME 类型上传该文件。生成的包含围绕文件的资源分支的文件。不是非常有用。

以下代码假设 MIME 类型在 $type 中,并且您已将文件内容加载到 $content 中。如果文件为 MacBinary 格式,它会深入到资源分支头,获取数据分支的长度(字节 83-86),并使用它来去除资源分支。

(可能还有更好的方法,但这解决了我的问题)

<?php
if ($type == 'application/x-macbinary') {
if (
strlen($content) < 128) die('文件太小');
$length = 0;
for (
$i=83; $i<=86; $i++) {
$length = ($length * 256) + ord(substr($content,$i,1));
}
$content = substr($content,128,$length);
}
?>
~caetin~ ( at ) ~hotpop~ ( dot ) ~com~
20 年前
来自手册

如果在表单中未选择任何文件进行上传,PHP 将返回 $_FILES['userfile']['size'] 为 0,并将 $_FILES['userfile']['tmp_name'] 为 none。

从 PHP 4.2.0 版本开始,“none” 已经不再可靠地表示没有上传文件。点击“错误代码”链接可以看到相关文档,但是你需要查看 `$_FILES['your_file']['error']`。如果值为 4,则表示没有选择文件。
am at netactor dot NO_SPAN dot com
22 年前
如果你使用重新编码字符的模块,你的二进制文件可能会上传错误。例如,对于俄罗斯 Apache,你应该使用
<Files ScriptThatReceivesUploads.php>
CharsetDisable On
</Files>
jan at lanteraudio dot nl
11 年前
我也遇到了 `max_file_size` 问题,特别是上传文件大小超过设置的 `upload_max_filesize` 时,没有任何响应,没有任何错误。

我发现问题不在于 `upload_max_filesize` 设置,而是 `post_max_size` 设置导致了无响应的问题。所以,如果你将 `post_max_size` 设置得比 `upload_max_filesize` 大得多,那么当文件大小超过 `upload_max_filesize` 但仍在 `post_max_size` 限制范围内时,你至少可能会收到错误响应。

希望这对大家有所帮助。
myko AT blue needle DOT com
19 年前
快速说明一下 Apache、隐藏表单字段 `MAX_FILE_SIZE` 和 `zlib.output_compression = On` 之间存在一个问题。即使 PHP 正确抛出 `MAX_FILE_SIZE` 错误,浏览器似乎也会继续上传整个文件。将 zlib 压缩关闭似乎可以解决这个问题。我没有时间深入研究到底是哪个环节出了问题,但想让其他人避免为此苦恼。
ceo at l-i-e dot com
19 年前
在我看来,示例代码中使用 `/var/www/uploads` 简直是犯罪。

绝对不应该将不受信任的文件上传到你的 web 目录树中,无论是在哪个服务器上。

在共享服务器上,你的 web 目录树中的任何目录都不应该具有足以成功上传文件的权限。共享服务器上的任何其他用户都可以编写 PHP 脚本将他们想要的内容转储到其中!

`$_FILES['userfile']['type']` 基本上是**无用的**。
A. 浏览器的 MIME 类型并不一致,因此你永远无法捕获任何给定文件格式的所有可能的类型组合。
B. 它可以被伪造,所以无论如何它都是糟糕的安全性。

你的代码应该**检查**实际文件以查看它是否看起来正常。

例如,可以快速轻松地使用 `imagegetsize` 来处理图像,并且你至少知道前 N 个字节看起来像图像。这不能保证它是一个有效的图像,但这使得它不太可能是一个可工作的安全漏洞文件。

对于基于 Unix 的服务器,可以使用 `exec` 和 `file` 命令来查看操作系统是否认为内部内容与你期望的数据类型一致。

过去我在读取文件上传中的 `/tmp` 文件时遇到过麻烦。如果 PHP 允许我在尝试 `move_uploaded_file` 之前读取该文件就好了,但是 PHP 不允许,大概是假设我会做一些危险的事情来读取不受信任的文件。好吧。应该将上传的文件移动到某个暂存目录。然后你尽可能彻底地检查其内容。然后,如果它看起来正常,则将其移动到 web 目录树外部的目录中。对该文件的任何访问都应通过读取文件的 PHP 脚本进行。即使经过你能想到的所有检查,将其放入你的 web 目录树中也仍然太危险了,在我看来。

这里有不止几篇用户贡献的笔记包含幼稚(糟糕的)建议。请谨慎。
info at levaravel dot com
15 年前
一个返回更易读的文件大小格式的小代码片段。

<?php

function display_filesize($filesize){

if(
is_numeric($filesize)){
$decr = 1024; $step = 0;
$prefix = array('Byte','KB','MB','GB','TB','PB');

while((
$filesize / $decr) > 0.9){
$filesize = $filesize / $decr;
$step++;
}
return
round($filesize,2).' '.$prefix[$step];
} else {

return
'NaN';
}

}

?>
david at cygnet dot be
18 年前
如果你在通过 SSL 连接将文件从 Internet Explorer 发布到 PHP 脚本时遇到问题,例如“无法显示页面”或 `$_FILES` 和 `$_POST` 数组为空(jason 在 2006 年 1 月 10 日 02:08 描述),请查看这篇 Microsoft 知识库文章

http://support.microsoft.com/?kbid=889334

这篇知识库文章解释了自服务包 2 以来,通过 SSL 从 IE 发布可能存在问题。值得检查你的问题是否是 IE 特定的,因为这绝对不是 PHP 问题!
jedi_aka at yahoo dot com
18 年前
对于那些尝试在 Windows XP/2000/XP Media 等上的 IIS 上使上传工作的人,这里有一个快速的操作步骤。

1) 在你的代码运行的同一目录下创建子目录“uploads/”后,使用 oportocala 上面的代码,并确保你尝试写入的文件被写入到该文件夹下。(我建议使用 `echo $uploadfile;` 打印它)

2) 在 Windows 资源管理器中浏览到上面创建的上传目录并共享它。为此,请执行以下子步骤。
a) 右键单击文件夹,单击“共享和安全…”
b) 选中“在网络上共享此文件夹”
c) 选中“允许网络用户更改我的文件”(此步骤非常重要)
d) 单击“确定”或“应用”

3) 然后你可以在 IIS 中设置它的读写权限。为此,请执行以下子步骤。
a) 打开 IIS(开始/控制面板(经典视图)/管理工具/Internet 信息服务)
b) 浏览到你的文件夹(我们上面创建的文件夹)
c) 右键单击并选择属性。
d) 在“目录”选项卡中,确保选中“读取”、“写入”和“目录浏览”。
e) 对于那些注重安全的人,你还应该确保“执行权限:”设置为“仅脚本”或更低(不要设置为“脚本和可执行文件”)(这是因为有人可以将脚本上传到你的目录并运行它。而且,你绝对不希望发生这种情况)。

就是这样。

如果它对你有用或没用,请给我反馈,以便我可以更新操作步骤。

[email protected]

PS:非常感谢 oportocala
xmontero at dsitelecom dot com
12 年前
如果“大文件”(例如:50 或 100 MB)上传失败,请检查以下内容

你的到服务器的出站连接可能很慢,它可能超时,不是“执行时间”而是“输入时间”,例如在我们的系统中默认为 60 秒。在我们的案例中,大型上传可能需要 1 或 2 个小时。

此外,我们还有应该在上传后保留的“会话设置”。

1) 你可能需要检查这些 ini 条目

* `session.gc_maxlifetime`
* `max_input_time`
* `max_execution_time`
* `upload_max_filesize`
* `post_max_size`

2) 仍然失败?注意,并非所有这些都可以从脚本本身更改。`ini_set()` 可能会无法覆盖。

更多信息请访问此处
https://php.net/manual/es/ini.list.php

你可以看到“upload_max_filesize”等是 `PHP_INI_PERDIR` 而不是 `PHP_INI_ALL`。这使其无法使用 `ini_set()`
https://php.net/manual/en/configuration.changes.modes.php

请改用 `.htaccess`。

3) 仍然失败?请确保你启用了“.htaccess”来覆盖你的 php 设置。这是在 apache 文件中完成的。你至少需要 `AllowOverride Options`。

请访问此处查看
https://php.net/manual/en/configuration.changes.php

如果你的主文件带有 `AllowOverride None`,则需要手动允许此操作。

结论

根据系统不同,要允许“大型文件上传”,你必须不断向上层级操作,直至修改 apache 配置。

示例文件

这些对我有用,用于持续 2 小时的 100MB 上传

在 apache-virtual-host 中
-----------------------------------------------------------
<Directory /var/www/MyProgram>
AllowOverride Options
</Directory>
-----------------------------------------------------------

在 .htaccess 中
-----------------------------------------------------------
php_value session.gc_maxlifetime 10800
php_value max_input_time 10800
php_value max_execution_time 10800
php_value upload_max_filesize 110M
php_value post_max_size 120M
-----------------------------------------------------------

在示例中:
- 由于最后需要 1 到 2 个小时,我允许 3 个小时 (3600x3)
- 由于我需要 100MB,我为文件留出一些余量 (110M),并为整个 post 留出更多余量 (120M)。
svenr at selfhtml dot org
17 年前
关于隐藏表单字段 `MAX_FILE_SIZE` 的说明

PHP 有一个有点奇怪的功能,可以检查多个“最大文件大小”。

两个广为人知的限制是 php.ini 设置“post_max_size”和“upload_max_size”,它们组合起来对可以接收的最大数据量施加硬限制。

除了这个,PHP 以某种方式实现了一个软限制功能。它检查名为“max_file_size”(大写也行)的表单字段是否存在,该字段应包含一个表示允许的最大字节数的整数。如果上传的文件大于此字段中的整数,则 PHP 将不允许此上传并在 $_FILES 数组中显示错误代码。

PHP 文档中也含糊地提到过(或者曾经提到过——参见 bug #40387 - http://bugs.php.net/bug.php?id=40387)“允许浏览器在上传前检查文件大小”。然而,这并非事实,也从未实现过。到今天为止,没有任何 RFC 建议使用这种命名的表单字段,也没有任何浏览器真正检查其存在性或内容,或阻止任何操作。PHP 文档暗示浏览器可能会警告用户上传文件过大——这完全是错误的。

请注意,使用此 PHP 功能并非明智之举。客户端可以轻松更改表单字段。如果必须检查文件大小,请在脚本中以常规方式进行检查,使用脚本定义的整数,而不是从 HTTP 客户端获取的任意数字(从安全角度来看,始终不应信任 HTTP 客户端)。
Rob
16 年前
您的网站根目录下不应该有任何具有文件上传所需权限的目录。如果您要进行文件上传,建议您结合使用 PHP FTP 函数和文件字段,这样文件将传输到服务器之外的远程 FTP 位置。
keith at phpdiary dot org
19 年前
警告:*不要*依赖 $_FILES['userfile']['type'] 来验证上传的文件类型;如果您这样做,您的服务器可能会受到攻击。我将在下面解释原因。

手册(如果您向上滚动)中指出:$_FILES['userfile']['type'] - 文件的 MIME 类型,如果浏览器提供了此信息。例如,“image/gif”。

请记住,此 MIME 类型很容易被伪造,因为 PHP 在验证它是否真的是最终用户报告的那样方面做得不够深入!

因此,有人可以将恶意的 .php 脚本上传为“image/gif”,并执行指向该“图像”的 URL。

我最好的建议是检查文件的扩展名并使用 exif_imagetype() 检查有效图像。许多人建议使用 getimagesize(),如果文件确实是图像,则返回一个数组,否则返回 false,但 exif_imagetype() 速度更快。(手册是这么说的)
damien from valex
15 年前
这是检查 POST 数据量过大的更简单方法(v3 from sonic-world.ru 方法的替代方案)。

<?php
if ($_SERVER['REQUEST_METHOD'] == 'POST' && empty($_POST) && $_SERVER['CONTENT_LENGTH'] > 0) {
throw new
Exception(sprintf('由于当前配置,服务器无法处理这么多 POST 数据(%s 字节)', $_SERVER['CONTENT_LENGTH']));
}
?>
geert dot php at myrosoft dot com
18 年前
如果文件名包含单引号,则文件名的一部分会丢失。
例如:上传文件名
startName 'middlepart' endName.txt
将被上传(并因此存储在 _Files ['userfile'] 变量中为
endName.txt
跳过第二个单引号之前的全部内容。
robpet at tds dot net
19 年前
有人指出,上传目录权限不正确可能会阻止照片或其他文件的上传。错误地设置目录的 Apache 所有者也会阻止文件上传——我使用一个 PHP 脚本,该脚本在将上传的文件放入目录之前(如果该目录尚不存在)创建一个目录。当脚本创建目录然后将上传的文件复制到目录中时,不会出现问题,因为文件的拥有者是 Apache 运行的任何用户,通常是“nobody”。但是,假设我已将站点移至新服务器,并使用 FTP 复制了现有文件目录。在这种情况下,所有者的名称将与 Apache 所有者不同,文件将无法上传。解决方法是使用 Telnet 登录到站点,并使用 CHOWN 命令将所有者重置为“nobody”或 Apache 运行的任何用户。
Thomas
12 年前
MIME 类型可以伪造。

VVV

$_FILES['userfile']['type']

文件的 MIME 类型,如果浏览器提供了此信息。例如,“image/gif”。但是,此 MIME 类型在 PHP 端未经检查,因此不要将其值视为理所当然。

https://php.net/manual/en/features.file-upload.post-method.php

[编者注:删除了对已删除注释的引用,并编辑了注释使其本身有意义。]
rnagavel at yahoo dot com dot au
16 年前
如果 $_FILES 始终为空,请检查表单的方法。
它应该是 POST。表单的默认方法是 GET。

<form action="myaction.php">
<input type="file" name"userfile">
</form>
文件将不会上传,因为表单的默认方法是 GET。

<form action="myaction.php" method="POST">
<input type="file" name"userfile">
</form>
文件将被上传,并且 $_FILES 将被填充。
Tyfud
19 年前
重要的是要注意,当使用 move_uploaded_file() 命令时,某些配置(尤其是 IIS)如果在目标路径前添加前导“/”,则会失败。请尝试以下操作

<?php move_uploaded_file($tmpFileName,'uploads/'.$fileName); ?>

设置权限也是必须的。确保所有帐户都对上传目录具有写入访问权限,如果您希望稍后查看这些文件,则具有读取访问权限。如果您仍然遇到访问错误,也可能需要之后使用 chmod() 对目录或文件进行修改。
olijon, iceland
20 年前
上传大型图像时,使用 Netscape 时出现“文档不包含数据”错误,使用 Explorer 时出现错误页面。我的服务器设置为 RH Linux 9、Apache 2 和 PHP 4.3。

我发现 httpd.conf 文件中缺少以下条目

<Files *.php>
SetOutputFilter PHP
SetInputFilter PHP
LimitRequestBody 524288 (最大大小,以字节为单位)
</Files>

添加此条目后,一切正常。

- Oli Jon,冰岛
warwickbarnes at yahoo dot co dot uk
19 年前
在 Microsoft IIS 上使用 PHP 时,您可能会遇到以下问题:即使所有文件夹权限似乎都正确,也会从 move_uploaded_file 函数收到权限被拒绝错误。我必须设置以下内容才能使其工作

1. 通过 IIS 管理控制台设置文件夹的写入权限。
2. 在文件夹的安全设置中为 IUSR_'server' 设置写入权限。
3. 在文件夹的安全设置中为“域用户”设置写入权限。

需要第三个设置,因为我的应用程序本身位于安全文件夹中——使用身份验证(基本身份验证或 Windows 集成身份验证)来识别用户。当上传发生时,IIS 似乎正在检查这些用户是否对该文件夹具有写入访问权限,而不仅仅是 Web 服务器 (IUSR_'server') 是否具有访问权限。

此外,请记住在 IIS 管理控制台中将“执行权限”设置为“无”,以防止用户上传脚本文件然后运行它。(也建议对上传的文件进行其他检查,但“无执行”是一个良好的开端。)
therhinoman at hotmail dot com
20 年前
如果您的上传脚本仅用于上传图像,您可以使用图像函数 getimagesize()(不需要 GD 图像库)来确保您确实获得了图像,并过滤图像类型。

<?php getimagesize($file); ?>

……如果文件不是图像或无法访问,则返回 false,否则返回一个数组……

<?php
$file
= 'somefile.jpg';

# 假设您已经采取了一些其他
# 预防措施,例如检查文件
# 扩展名…

$result_array = getimagesize($file);

if (
$result_array !== false) {
$mime_type = $result_array['mime'];
switch(
$mime_type) {
case
"image/jpeg":
echo
"文件是 jpeg 类型";
break;
case
"image/gif":
echo
"文件是 gif 类型";
break;
default:
echo
"文件是图像,但不是 gif 或 jpeg 类型";
}
} else {
echo
"文件不是有效的图像文件";
}
?>

结合使用此函数和本页上提到的其他函数,可以使图像加载几乎万无一失。

有关支持的图像类型和更多信息,请参见 https://php.net/manual/en/function.getimagesize.php
maya_gomez ~ at ~ mail ~ dot ~ ru
20 年前
上传文件时,$_FILES['file']['name'] 包含其转换为服务器默认字符集的原始名称。
如果名称包含默认字符集中不存在的字符,则转换失败,并且 $_FILES['file']['name'] 保持原始字符集。

从 windows-1251 环境上传到 koi8-r 时,我遇到了这种行为。如果文件名包含数字符号“?”(0xb9),则只要 koi8-r 中没有此类字符,它就不会被转换。

我使用的解决方法

<?php
if (strstr ($_FILES['file']['name'], chr(0xb9)) != "")
{
$_FILES['file']['name'] = iconv (
"windows-1251",
"koi8-r",
str_replace (chr(0xb9), "N.", $_FILES['file']['name']));
};
?>
mariodivece at bytedive dot com
19 年前
只想指出一个细节,一些人可能会感兴趣。

当使用 base64_encode 将二进制数据存储到数据库中时,数据大小会增加 1.33 倍。有一种更好的方法可以直接存储数据。尝试以下方法:

<?php $data = mysql_real_escape_string($data); ?>

这将保持数据不变,并以正确的方式格式化,可以直接插入 MySQL 语句中,而不会浪费空间。

顺便说一下,我要感谢 therebechips 提供的关于数据块的优秀建议。
djot at hotmail dot com
19 年前
-
小心通过以下方式设置 max_file_size:
<?php ini_get('upload_max_filesize'); ?>

ini_get 可能会返回类似 "2M" 的值,这将导致上传失败。

在我的例子中,这就是“错误之处”。

<?php
$form
= '<input type="hidden" name="MAX_FILE_SIZE" value=".ini_get('upload_max_filesize')." />';
?>

文件已上传到服务器,但没有任何上传信息,甚至没有错误消息。$_FILES 完全为空。

djot
-
Leevi at izilla dot com dot au
19 年前
这可能有助于文件上传新手……我从朋友那里得到了建议才解决这个问题……

如果您正在使用:
- Windows XP
- IIS 5
- PHP 5

如果您不断收到文件上传的权限错误……并且您已经确认您已在 IIS 中设置了该目录的写入权限……

请再次检查:
a) 在 Windows 资源管理器中,单击“工具”>“文件夹选项”
单击“查看”选项卡
向下滚动到“使用简单文件共享 (推荐)”
取消选中此复选框

b) 找到您希望上传到的服务器上的文件夹
c) 单击“属性”,然后单击“安全”选项卡
d) 确保选中相应的写入设置。

您可以尝试将“所有人”设置为具有完全权限……

注意:这样做会在您的服务器上打开很大的安全漏洞……

希望这有帮助

Leevi Graham
Phil Ciebiera
15 年前
在使用 IIS 的 Microsoft 平台上,您可能会遇到这种情况:移动上传的文件后,匿名 Web 用户无法访问内容,除非先提示进行身份验证……

原因是,上传的文件将继承 php.ini 中 upload_tmp_dir 指令指定的目录的权限。如果未设置此指令,则使用默认的 C:\Windows\Temp。

您可以通过授予 IUSR_[服务器名称] 用户对临时上传目录的读取访问权限来解决此问题,以便在您 move_uploaded_file 后,权限已正确设置。

出于安全原因,最好将上传目录的执行权限设置为不包含可执行文件。
为此:
- 打开 IIS 管理器
- 浏览到将放置上传文件的相关站点目录
- 右键单击文件夹并选择“属性”
- 在生成的对话框的“目录”选项卡中,将执行权限设置为“无”

我花了一段时间才弄清楚这一点,所以我希望这能帮助节省其他人的时间。
jahajee
16 年前
嗨,我在使用 upload_max_filesize 时遇到困难,如果将最大文件大小设置为小于 PHP 设置的大小,则您的脚本报告错误的功能仅在您的最大设置文件大小和 PHP 设置的最大大小之间的差值范围内有效。因此,如果上传的文件超过 PHP 最大文件大小,则 PHP 将突然结束,而不会留下任何错误痕迹,也就是说,它的行为就像没有上传任何文件一样,因此没有报告任何错误。当然,如果上传文件对于表单是可选的,那么上传较大文件的用户将不会收到任何错误,并且表单仍然会处理,只是没有文件。
GET 方法不能用于可选上传。即使在错误报告中也找不到帮助。小心处理可选上传。
jahajee
To Top