使用PHP创建单文件上传表单的最佳方法是什么?

21

我在网上找到了一些样例,但我想从每天使用PHP的人那里得到反馈,以了解可能存在的安全或性能问题及其解决方案。

请注意,我只对一次上传一个文件感兴趣。

最好不需要浏览器插件(Flash / Java),尽管了解使用插件的好处是很有意思的。

我想了解最佳的HTML表单代码和PHP处理代码。

4个回答

34

文件上传教程

HTML

<form enctype="multipart/form-data" action="action.php" method="POST">
  <input type="hidden" name="MAX_FILE_SIZE" value="1000000" />
  <input name="userfile" type="file" />
  <input type="submit" value="Go" />
</form>
  • action.php是一个将处理上传的PHP文件的名称(如下所示)
  • MAX_FILE_SIZE必须出现在具有类型file的输入框之前。该值可以很容易地在客户端上进行操作,因此不应依赖它。它的主要好处是在用户上传文件之前提供了过大警告。
  • 您可以更改具有类型file的输入框的名称,但请确保它不包含任何空格。您还必须更新PHP文件中相应的值(如下所示)。

PHP

<?php
$uploaddir = "/www/uploads/";
$uploadfile = $uploaddir . basename($_FILES['userfile']['name']);

echo '<pre>';
if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {
    echo "Success.\n";
} else {
    echo "Failure.\n";
}

echo 'Here is some more debugging info:';
print_r($_FILES);
print "</pre>";
?>

上传文件夹不应该位于可以通过HTTP访问的位置,否则可能会上传PHP脚本并在服务器上执行。

打印$_FILES的值可以提示正在发生的情况。例如:

    Array
    (
        [userfile] => Array
        (
            [name] => Filename.ext
            [type] => 
            [tmp_name] => 
            [error] => 2
            [size] => 0
        )
    )

此结构提供了有关文件名称、MIME类型、大小和错误代码的一些信息。

错误代码

0表示没有错误,文件已成功上传
1表示文件超过了php.ini中定义的最大文件大小。如果想更改最大文件大小,需要打开php.ini文件,找到以下行:upload_max_filesize = 2M,并将值从2M(2MB)更改为所需值
2表示手动定义的最大文件大小在页面脚本内已经超过了限制
3表示文件只被部分上传
4表示文件未指定(空文件字段)
5尚未定义
6表示没有临时文件夹
7表示无法将文件写入磁盘

php.ini配置

使用较大文件运行此设置时,您可能会收到错误消息。检查php.ini文件中是否有以下键:

max_execution_time = 30
upload_max_filesize = 2M

适当增加这些值可能会有所帮助。在使用Apache时,更改此文件需要重新启动。

允许的最大内存值(通过memory_limit设置)在此处没有作用,因为文件在上传时将被写入tmp目录。可以通过upload_tmp_dir可选地控制tmp目录的位置。

检查文件MIME类型

应该检查用户正在上传的文件类型 - 最佳实践是针对允许的文件类型列表进行验证。允许任何文件的潜在风险是用户可能会将PHP代码上传到服务器,然后运行它

您可以使用非常有用的fileinfo扩展名(取代了旧的mime_content_type函数)来验证MIME类型。

// FILEINFO_MIME set to return MIME types, will return string of info otherwise
$fileinfo = new finfo(FILEINFO_MIME);
$file = $fileinfo->file($_FILE['filename']);

$allowed_types = array('image/jpeg', 'image/png');
if(!in_array($file, $allowed_types))
{
    die('Files of type' . $file . ' are not allowed to be uploaded.');
}
// Continue

更多信息

您可以在PHP.net手册上阅读有关处理文件上传的更多信息。

针对PHP 5.3及以上版本

//For those who are using PHP 5.3, the code varies.
$fileinfo = new finfo(FILEINFO_MIME_TYPE);
$file = $fileinfo->file($_FILE['filename']['tmp_name']);
$allowed_types = array('image/jpeg', 'image/png');
if(!in_array($file, $allowed_types))
{
    die('Files of type' . $file . ' are not allowed to be uploaded.');
}
// Continue

更多信息

您可以在PHP.net文档中阅读有关FILEINFO_MIME_TYPE的更多信息。


1
值得注意的是,在这个例子中,我可以上传一个 PHP 文件并访问您的 Web 服务器。小心处理这些东西。:S - Paolo Bergantino
没错,我把这个作为社区答案发布了,所以你可以自由地进行编辑 :) - Drew Noakes
是的,检查您上传的文件的MIME类型非常重要。最好的保护措施是允许某些类型(如image/jpeg、archive/zip等),并禁止所有其他类型。 - Ross
@Ross:听起来是个好主意。你能演示一些检查MIME类型的代码吗?顺便感谢你提供的链接。 - Drew Noakes
1
我使用FileInfo扩展来检查MIME类型。将其添加到此帖子中(因为它更好),并稍微更改格式。 - Ross
很棒的更新,Ross,谢谢。我也从你那里学到了一些Markdown。 - Drew Noakes

5

阅读这篇介绍,它应该告诉你需要知道的一切。用户评论也相当有用。


4

Flash的主要优点在于它允许你上传多个文件。而Java的主要优点则是它允许从文件系统中进行拖放操作。很抱歉我没有回答中心问题,但我认为这非常简单。


另一个好处是,在PHP 4中无法显示上传进度条,但在PHP 5、Flash和Java中可以。 - David
1
不要只谈论Java和Flash,如果你能提供使用PHP5演示如何显示进度条的示例代码,那会更有用;) - Milan Babuškov
你可以访问以下链接:http://www.phpclasses.org/blog/post/61-File-upload-progress-meter-for-PHP-4-at-last.html - eyelidlessness
你可以使用PHP支持的HTML数组功能上传多个文件。PHP手册 - Mido

3
安全性在文件上传方面非常重要,为上传文件夹添加一个 .htaccess 文件可以防止脚本被运行,这样可以额外增加一层安全保障。
.htaccess
Options -Indexes
Options -ExecCGI
AddHandler cgi-script .php .php3 .php4 .phtml .pl .py .jsp .asp .htm .shtml .sh .cgi 

参考:http://www.mysql-apache-php.com/fileupload-security.htm

这篇文章主要介绍了如何确保文件上传的安全性,包括限制文件类型、大小和使用合适的文件命名等。此外,还需要对上传的文件进行验证,并避免直接在服务器上执行上传的文件。通过采取这些措施,可以有效地提高文件上传的安全性,防止恶意攻击。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接