php - Nginx PHP 因大文件上传(超过 6 GB)而失败

标签 php nginx file-upload amazon-s3 hhvm

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

  1. 文件通过 Ajax 上传到 PHP 脚本。
  2. PHP 上传脚本获取 $_FILE 并将其分块复制,如 this answer 所示。到临时位置。
  3. 文件的位置存储在数据库中
  4. cron 脚本稍后会将文件上传到 s3,再次使用 fopen 函数和缓冲来保持较低的内存使用量

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

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

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

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

最佳答案

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

根据developer.mozilla.org在 multipart/form-data 正文中,HTTP Content-Disposition 通用 header 是可在多部分正文的子部分上使用的 header ,以提供有关其适用的字段的信息。子部分由 Content-Type header 中定义的边界分隔。用于正文本身,Content-Disposition 没有任何效果。

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

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

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

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

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

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-Name header 中的文件名。

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

client_body_in_file_only on;

让我们看看如何实现它:

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

2) 创建新的后端端点 http://backend/file处理临时文件名和 header X-File-Name

之间的映射

4) 带有 header X-File-Name 的仪器 AJAX 查询,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 configuration option client_body_in_file_only is incompatible with multi-part data upload, but you can use it with AJAX i.e. XMLHttpRequest2 (binary data).

如果需要后端验证,唯一的处理方法是使用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 on; auth_request on;

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

关于php - Nginx PHP 因大文件上传(超过 6 GB)而失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44371643/

相关文章:

javascript - 如何在 wordpress 中使用 php 函数(在 functions.php 中)返回的值分配一个 javascript 变量(在页面上)

php - ZeroMQ 和 PHP-FPM

node.js - 使用 electron 或 node.js 将文件存储在本地驱动器文件夹中

angular - 使用 ng2-Uploader (Angular2) & .net core 上传文件

php - jQuery toggle() 在包含的(使用 PHP)代码中不起作用。

php - 使用 cURL 和 PHP 为同一键和文件上传混合多个值

php - 具有重复行的 MySQL IN()?

reactjs - 如何在没有域名(仅 IP)的服务器上使用 nginx 服务 react-create-app 构建?

nginx - 为 Kubernetes 入口保留客户端源 IP?

javascript - 尝试使用 ng-file-upload 和多方上传文件来上传文件