大file upload导致Nginx PHP失败(超过6 GB)

我有一个非常奇怪的问题上传大于6GB的大文件。 我的过程是这样的:

  1. 文件通过Ajax上传到php脚本中。
  2. PHP上传脚本需要$ _FILE并将其复制到块中,就像在tmp位置的这个答案中一样。
  3. 文件的位置存储在数据库中
  4. cron脚本会在稍后将file upload到s3,再次使用fopen函数和缓冲来保持内存使用率低

我的PHP(HHVM)和NGINXconfiguration都configuration为允许高达16GB的文件,我的testing文件只有8GB。

这是怪异的部分,阿贾克斯总是会超时。 但是文件已经成功上传了,它被拷贝到了tmp的位置,存储在db,s3等等的位置。但是AJAX运行了一个小时,甚至在所有的执行完成之后(需要10-15分钟)超时时结束。

什么可以导致服务器不发送大文件的响应?

另外服务器端的错误日志是空的。

一个大文件上传是昂贵的和容易出错的操作。 Nginx和后端应该有正确的超时边界来处理缓慢的磁盘IO(如果有的话)。 从理论上讲,使用多部分/表单数据编码RFC 1867来管理文件上传是非常简单的。

根据developer.mozilla.org中的多部分/表单数据体,HTTP Content-Disposition一般头部是一个头部,可以在多部分主体的子部分上使用,以提供有关应用的字段的信息。 子部分由Content-Type头部中定义的边界分隔。 对于身体本身,Content-Disposition没有任何作用。

让我们看看文件上传时会发生什么:

1)客户端发送带有文件内容的HTTP请求到Web服务器

2)web服务器接受请求并启动数据传输(如果文件大小超过限制,则返回错误413)

3)Web服务器开始填充缓冲区(取决于文件和缓冲区的大小)

4)网络服务器通过文件/网络套接字将文件内容发送到后端

5)后端验证初始请求

6)后端读取文件并剪切标题(Content-Disposition,Content-Type)

7)后端转储结果文件到磁盘上

8)任何后续程序,如数据库更改

client_body_in_file_only关闭;

在大文件上传期间会出现几个问题:

  • HTTP正文请求转储到磁盘并传递到后端的进程和复制文件
  • 在HTTP请求内容上传到服务器之前,不可能验证请求
  • 而上传大文件后端很少需要立即为文件内容本身

让我们从配置了新位置http:// backend / upload的 Nginx开始接收大文件上传,后端交互被最小化(Content-Legth:0),文件正在存储到磁盘。 使用缓冲区Nginx将文件转储到磁盘(一个文件存储到临时目录,随机的名称,它不能被改变)后面的POST请求后端到位置http://后端/文件的文件名在X-File-名称标题。

要保留额外的信息,您可以使用包含初始POST请求的标头。 例如,从最初的请求中获得X-Original-File-Name头文件可以帮助您匹配文件并将必要的映射信息存储到数据库中。

client_body_in_file_only;

让我们看看是如何发生的:

1)配置Nginx将HTTP正文内容转储到文件并保存client_body_in_file_only;

2)创建新的后端端点http://后端/文件来处理临时文件名和标题之间的映射X-File-Name

4)仪器带头文件的 AJAX查询X-File-Name Nginx将使用发送文件上传请求

组态:

location /upload { client_body_temp_path /tmp/; client_body_in_file_only on; client_body_buffer_size 1M; client_max_body_size 7G; proxy_pass_request_headers on; proxy_set_header X-File-Name $request_body_file; proxy_set_body off; proxy_redirect off; proxy_pass http://backend/file; } 

Nginx配置选项client_body_in_file_only与多部分数据上传不兼容,但是可以使用AJAX即XMLHttpRequest2(二进制数据)。

如果您需要进行后端身份验证,则唯一的办法就是使用auth_request ,例如:

 location = /upload { auth_request /upload/authenticate; ... } location = /upload/authenticate { internal; proxy_set_body off; proxy_pass http://backend; } 

client_body_in_file_only; auth_request on;

预先上传身份验证逻辑可防止未经身份验证的请求,而不管初始POST内容长度大小。