Nginx PHP失败,大文件上传(超过6 GB)

Dev*_*xon 14 php file-upload nginx amazon-s3 hhvm

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

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

我的PHP(HHVM)和NGINX配置都将其配置设置为允许最多16GB的文件,我的测试文件只有8GB.

这是奇怪的部分,ajax 总是会超时.但是文件被成功上传,它被复制到tmp位置,存储在db,s3中的位置等等.但是即使所有执行完成(需要10-15分钟),AJAX也会运行一个小时超时时结束.

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

服务器端的错误日志也是空的.

Ana*_*oly 48

大文件上传是一种昂贵且容易出错的操作.Nginx和后端应配置正确的超时,以便在发生时处理慢速磁盘IO.从理论上讲,使用multipart/form-data encoding RFC 1867管理文件上传是很简单的.

根据developer.mozilla.org在multipart/form-data主体中,HTTP Content-Disposition通用标头是一个标头,可以在多部分主体的子部分上使用,以提供有关其应用的字段的信息.子部分由Content-Type标头中定义的边界分隔.在身体本身使用,Content-Disposition没有效果.

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

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

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

3)webserver开始填充缓冲区(取决于文件和缓冲区大小)

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

5)后端验证初始请求

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

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

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

client_body_in_file_only off;

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

  • HTTP正文请求转储到磁盘并传递到后端,后端处理并复制该文件
  • 在将HTTP请求内容上载到服务器之前,无法对请求进行身份验证
  • 上传大文件后端很少立即需要文件内容

让我们从Nginx配置新位置http:// backend/upload开始接收大文件上传,后端交互最小化(Content-Legth:0),文件只存储到磁盘.使用缓冲区Nginx将文件转储到磁盘(存储在临时目录中的文件,随机名称,无法更改),然后POST请求后端到位置http:// backend/file,文件名在X-File-中名称标题.

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

client_body_in_file_only on;

让我们看看它是如何实现的:

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

2)创建新的后端端点http:// backend/file来处理临时文件名和头之间的映射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;
}
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

client_body_in_file_only on;  auth_request on;

无论初始POST内容长度大小如何,预上载身份验证逻辑都可以防止未经身份验证的请求.