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

时间:2017-06-05 14:49:53

标签: php nginx file-upload amazon-s3 hhvm

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

  1. 文件通过Ajax上传到php脚本。
  2. PHP上传脚本获取$ _FILE并将其以块的形式复制,如this answer中所示,将其复制到tmp位置。
  3. 文件的位置存储在db
  4. cron脚本稍后会将文件上传到s3,再次使用fopen函数和缓冲来降低内存使用率
  5. 我的PHP(HHVM)和NGINX配置都将其配置设置为允许最多16GB的文件,我的测试文件只有8GB。

    这是奇怪的部分,ajax将总是超时。但是文件成功上传,它被复制到tmp位置,存储在db,s3等位置。但是AJAX运行了一个小时甚至所有执行完成后(需要10-15分钟) )并且仅在超时时结束。

    什么原因导致服务器不能仅为大文件发送响应?

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

1 个答案:

答案 0 :(得分:41)

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

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

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

1)客户端将带有文件内容的HTTP请求发送到webserver

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

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

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

5)后端验证初始请求

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

7)后端转储导致文件到磁盘

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

client_body_in_file_only off;

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

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

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

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

client_body_in_file_only on;

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

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

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

4)具有标题 X-File-Name 的工具AJAX查询Nginx将使用

发送上传后请求

配置:

let
  

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

如果您需要进行后端身份验证,则只能使用auth_request,例如:

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;
}

client_body_in_file_only on; auth_request on;

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