使用PHP进行大文件上传

37

我想允许我们的 PHP 应用程序上传非常大的文件(几百兆字节至8吉字节)。然而,这里面有一些问题。

浏览器:

  • HTML 上传反馈很差,我们需要轮询上传进度(有点傻),或者根本不显示反馈信息
  • Flash 上传器在开始上传之前将整个文件放入内存中

服务器:

  • PHP 强制我们设置 post_max_size,这可能导致易受攻击的 DOS 攻击。我不想全局设置此选项。
  • 服务器还需要一些其他变量在 POST 变量中,例如一个秘密密钥。我们希望能够立即拒绝请求,而不是在整个文件上传后再拒绝请求。

要求:

  • HTTP 必须支持。
  • 客户端技术我很灵活,只要它在浏览器中运行就可以。
  • 如果有其他在 Linux 环境下工作良好的技术,PHP 不是必需的,那就很好。

1
如果PHP内部不缓冲整个输入,您可以编写一个模块,在POST正文之前检查标头中的ID与数据库中的ID是否匹配。在另一个端口上运行特殊服务器,并从主Web服务器生成ID。 - Aiden Bell
我想看看当许多网络用户同时上传8GB文件时会发生什么。 - Elzo Valugi
@ElzoValugi:这是一个认真的问题吗?如果是,也许你可以稍微改一下措辞,这样我就可以帮你回答了。 - Evert
1
好的,所以这是一种不友善的讽刺意味。明白了。幸运的是,我已经成功地在'09解决了这个问题。 - Evert
1
pluploader非常适合这个任务。因为它默认将上传分块为1MB,所以您可以绕过PHP大小限制。 - artfulrobot
显示剩余7条评论
13个回答

14

upload_max_filesize可以在每个目录上进行设置;post_max_size也是如此

e.g.:

<Directory /uploadpath/>
  php_value upload_max_filesize 10G
  php_value post_max_size 10G
</IfModule>

6
这是否意味着你实际上需要服务器上的10G RAM,还是这只是为了避免上传攻击/错误而进行的设置? - Martin Wickman
@MartinWickman,根据我进行的测试,似乎并不是这样。当然,除非你打开文件。我能够上传一个500MB的文件,包括move_uploaded_file,而只使用了0.2MB,根据memory_get_usage(TRUE) - artfulrobot
@artfulrobot的memory_get_usage遗漏了很多--例如,如果您从数据库中选择了1GB的数据,则在top中,您将看到内存使用量增加了约1GB,但是memory_get_usage和PHP的内存限制不会计算其中任何内容,直到您将其从数据库资源提取到本地PHP变量中。我建议在上传过程中观察top中Apache的内存利用率以确保。 - Frank Farmer
1
@FrankFarmer 我在 top 中观察了 Apache,上传了一个 1GB 的文件,但没有增加。 - artfulrobot

7

我知道这已经过时了,但现在也许有人仍然遇到这个问题。现在你可以只使用Javascript和PHP来完成,无需在客户端使用Flash或Java。

演示:http://dnduploader.filkor.org/

思路是使用Javascript的Blob slice()方法对文件进行切片...


7

Python处理程序?

使用Python POST处理程序而不是PHP。从您的PHP应用程序生成一个唯一标识符,客户端可以将其放入HTTP头中。使用mod_python在整个POST正文传输之前拒绝或接受大型上传。

我认为http://www.modpython.org/live/current/doc-html/dir-handlers-hph.html

允许您检查标题并拒绝POST输入的其余部分。我没有尝试过,但可能是正确的方法?

查看mod_python的源代码,通过read()缓冲输入似乎允许逐位评估HTTP输入。首先是标题。

https://svn.apache.org/repos/asf/quetzalcoatl/mod_python/trunk/src/filterobject.c


1
这似乎是目前为止发布的问题的唯一真正解决方案。 - Benji XVI

3

Java小程序怎么样?这是我之前工作的一家公司所采用的方式。我知道小程序很糟糕,特别是在如今我们拥有各种选择的时代,但它们确实是在Web开发中遇到类似桌面问题最通用的解决方案。这只是一个值得考虑的建议。


1
Java小程序可能能解决问题,但这只是问题的一半。 - Evert
1
WordPress使用基于Flash的上传工具。 - Tyler Carter

2

请查看jumploader.com

这是一个很好的Java小程序,用于上传文件。

我已经用它上传了图片,它可以正常工作。虽然我没有尝试过大于10MB的文件,但我相信它也适用于非常大的文件。


2

2

你可以为一个目录中的脚本设置post_max_size。将您的上传脚本放置在那里,并允许仅该脚本处理大文件大小。虽然该脚本仍可能受到大/无用文件的攻击,但它避免了全局设置。

与APC一起使用,您可能会想出一些好方法:IBM Developer works对APC的文章


APC在我们的负载均衡设置中使用起来很困难。我们不使用cookie固定,因此为了正确使用它,我们需要轮询实际上传文件的服务器(在我们的情况下有点糟糕)。仅在一个目录上设置post_max_size对我来说也没有用,因为它仍然容易受到该目录的DOS攻击,并且我想在请求开始时如果包含无效的GET数据就阻止请求。 - Evert

1

你有考虑过使用APC来检查进度和总文件大小吗?这里有一篇不错的博客文章,或许可以帮到你。


APC技巧需要轮询,但由于我们的负载均衡场景,我不喜欢这种方式。 - Evert
你无法进行轮询,因为每个轮询请求可能会被设置到与下载开始的服务器不同的服务器上。 - Peter D
你可以将投票密钥存储在数据库中。 - Aiden Bell
Aiden,这是不可能的,因为即使使用密钥,我只能在上传开始的服务器上请求上传信息。 - Evert
啊,我明白了。如果您有一个内部脚本来轮询正确的服务器并将这些结果存储在中央位置,并在前端上设置一个处理程序,那该怎么办? - Aiden Bell

1

Javascript 不允许我读取本地文件的内容。我不确定 'webdavsystem' 是如何做到的,但我认为他们仍然使用标准上传,并在服务器上有一个特殊的处理程序。 - Evert

1

我会研究一下FTP、SSH或SCP,这样你可以上传大文件并且仍然对文件进行访问控制。虽然可能需要花费一些时间来实施,但这可能是我能想到的最安全的方式。


我们不想真的走这条路... HTTP很简单,所以我们不想让环境过于复杂化。我们愿意在服务器端使用除PHP之外的其他东西,但HTTP是必须的。 - Evert

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