大文件上传是一项昂贵且容易出错的操作。 Nginx 和后端应该配置正确的超时来处理慢速磁盘 IO(如果发生)。从理论上讲,使用多部分/表单数据编码 RFC 1867 管理文件上传很简单。
根据 multipart/form-data 正文中的developer.mozilla.org,HTTP Content-Disposition 通用标头是一个标头,可用于多部分正文的子部分,以提供有关其适用的字段的信息。子部分由 Content-Type 标头中定义的边界分隔。用于 body 本身,Content-Disposition 无效。
让我们看看上传文件时会发生什么:
1) 客户端向网络服务器发送带有文件内容的 HTTP 请求
2) 网络服务器接受请求并启动数据传输(如果文件大小超过限制,则返回错误 413)
3) 网络服务器开始填充缓冲区(取决于文件和缓冲区大小)
4) 网络服务器通过文件/网络套接字将文件内容发送到后端
5) 后端验证初始请求
6) 后端读取文件并切头(Content-Disposition,Content-Type)
7) 后端将结果文件转储到磁盘上
8) 任何后续程序,例如数据库更改
在大文件上传过程中会出现几个问题:
- HTTP 正文请求转储到磁盘并传递到后端哪个进程和复制文件
- 在 HTTP 请求内容上传到服务器之前无法对请求进行身份验证
- 虽然上传大文件后端很少需要文件内容本身立即
让我们从配置新位置 http://backend/upload 的 Nginx 开始,以接收大文件上传,最小化后端交互(Content-Legth:0),文件仅存储到磁盘。使用缓冲区 Nginx 将文件转储到磁盘(以随机名称存储到临时目录中的文件,不能更改),然后向后端发送 POST 请求到位置 http://backend/file,文件名在 X-File-Name 标题。
为了保留额外信息,您可以在初始 POST 请求中使用标头。例如,初始请求中的 X-Original-File-Name 标头可帮助您匹配文件并将必要的映射信息存储到数据库中。
让我们看看如何实现它:
1) 配置 Nginx 以将 HTTP 正文内容转储到文件并保持存储在 client_body_in_file_only 上;
2) 创建新的后端端点http://backend/file 来处理临时文件名和头文件之间的映射X-File-Name
4) 带有 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 配置选项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;
}
无论初始 POST Content-Length 大小如何,预上传身份验证逻辑都可以防止未经身份验证的请求。