upload-progress: NGINX 上传进度跟踪模块
安装
您可以在任何基于 RHEL 的发行版中安装此模块,包括但不限于:
- RedHat Enterprise Linux 7、8、9 和 10
- CentOS 7、8、9
- AlmaLinux 8、9
- Rocky Linux 8、9
- Amazon Linux 2 和 Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install nginx-module-upload-progress
yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install nginx-module-upload-progress
通过在 /etc/nginx/nginx.conf 顶部添加以下内容来启用该模块:
load_module modules/ngx_http_uploadprogress_module.so;
本文档描述了 nginx-module-upload-progress v0.9.4,于 2025 年 3 月 15 日发布。
介绍
nginx_uploadprogress_module 是一个上传进度系统的实现,它监控 RFC1867 POST 上传在传输到上游服务器时的进度。
它通过跟踪 Nginx 代理到上游服务器的上传来工作,而无需分析上传的内容,并提供一个 Web API 以在 Javascript、JSON 或任何其他格式中报告上传进度(通过模板的帮助)。
它之所以有效,是因为 Nginx 作为上游服务器的加速器,在将上传的 POST 内容传输到上游服务器之前,将其存储在磁盘上。每个单独的 POST 上传请求应包含一个唯一的进度标识符。
该模块版权 (c) 2007-2012 Brice Figureau,并根据 BSD 许可证授权。
- rbtree 和 shm_zone 代码基于 Igor Sysoev 的 limit_zone Nginx 模块。
- expire header 代码基于 Igor Sysoev 的 header_filter Nginx 模块。
JSON 的想法和机制的想法基于 Lighttpd mod_uploadprogress: http://blog.lighttpd.net/articles/2006/08/01/mod_uploadprogress-is-back
警告: 当使用 --with-debug 编译时,该模块将生成大量日志消息。
不兼容的更改
v0.9.0:
JSONP 现在是进度探测的默认输出。如果您依赖于该模块提供已弃用的 java 输出,请使用:
upload_progress_java_output
在进度探测位置中。
配置
每个上传请求应分配一个唯一标识符。该唯一标识符将用于存储请求并引用以报告。
该标识符可以作为 GET 参数或名为 X-Progress-ID 的 HTTP 头传输。
upload_progress
| 语法 | upload_progress <zone_name> <zone_size> |
| 默认 | 无 |
| 上下文 | http |
该指令启用上传进度模块,并为 <zone_name> 保留 <zone_size> 字节,用于存储每个连接的跟踪信息。
track_uploads
| 语法 | track_uploads <zone_name> <timeout> |
| 默认 | 无 |
| 上下文 | location |
该指令启用当前位置的上传跟踪。每个到达此位置的 POST 将在 <zone_name> 上传进度跟踪器中注册请求。
由于 Nginx 目前尚不支持 RFC 1867 上传,因此位置必须是 proxy_pass 或 fastcgi 位置。
POST 必须 有一个名为 X-Progress-ID 的查询参数(或同名的 HTTP 头),其值是用于获取进度信息的唯一标识符。如果 POST 没有此信息,则不会跟踪上传。
跟踪的连接在完成后最多保留 <timeout> 秒,以便能够为上传进度探测提供有用的信息。
警告: 此指令必须是位置的最后一条指令。它必须位于 proxy_pass 或 fastcgi_pass 位置中。
report_uploads
| 语法 | report_uploads <zone_name> |
| 默认 | 无 |
| 上下文 | location |
该指令允许位置报告由 track_uploads 跟踪的 <zone_name> 的上传进度。
返回的文档是一个 Javascript 文本,默认可能有 4 种结果:
-
上传请求尚未注册或未知:
new Object({ 'state' : 'starting' }) -
上传请求已结束:
new Object({ 'state' : 'done' }) -
上传请求生成了 HTTP 错误:
一个可以用于跟踪客户端的错误代码是 413(请求实体过大)。new Object({ 'state' : 'error', 'status' : <error code> }) -
上传请求正在进行中:
new Object({ 'state' : 'uploading', 'received' : <size_received>, 'size' : <total_size>})
可以返回纯 JSON 而不是此 Javascript(请参见 upload_progress_json_output)。
也可以使用指令 upload_progress_template 完全配置响应格式。
对该位置的 HTTP 请求必须具有包含有效唯一标识符的 X-Progress-ID 参数或 HTTP 头,该标识符表示正在进行的上传。
upload_progress_content_type
| 语法 | upload_progress_content_type <content_type> |
| 默认 | text/javascript |
| 上下文 | location |
该指令允许更改上传进度探测响应的内容类型。
upload_progress_header
| 语法 | upload_progress_header <progress-id> |
| 默认 | X-Progress-ID |
| 上下文 | location |
该指令允许更改进度 ID 的头名称。
upload_progress_jsonp_parameter
| 语法 | upload_progress_jsonp_parameter <callback_parameter> |
| 默认 | callback |
| 上下文 | location |
该指令允许更改带有 jsonp 回调名称的 GET 参数的名称。
upload_progress_java_output
| 语法 | upload_progress_java_output |
| 默认 | N/A |
| 上下文 | location |
该指令将所有输出设置为 eval() 兼容的 javascript 代码。
upload_progress_json_output
| 语法 | upload_progress_json_output |
| 默认 | N/A |
| 上下文 | location |
该指令将所有输出设置为纯 JSON。
upload_progress_jsonp_output
| 语法 | upload_progress_jsonp_output |
| 默认 | N/A |
| 上下文 | location |
该指令将所有输出设置为 JSONP(类似于 JSON 输出,但带有回调)。
upload_progress_template
| 语法 | upload_progress_template <state> <template> |
| 默认 | 无 |
| 上下文 | location |
该指令可用于安装进度响应模板。 可用的状态列表包括:
startinguploadingerrordone
Nginx 将用上传的相应值替换以下变量的值:
$uploadprogress_length: 上传的总大小$uploadprogress_received: 服务器迄今为止接收到的内容$uploadprogress_status: HTTP 错误时的错误代码$uploadprogress_callback: 如果作为名为 'callback' 的 GET 查询参数提供,则为 jsonp 回调名称
例如,要返回 XML(而不是默认的 Javascript 或 JSON):
upload_progress_content_type 'text/xml';
upload_progress_template starting '<upload><state>starting</state></upload>';
upload_progress_template uploading '<upload><state>uploading</state><size>$uploadprogress_length</size><uploaded>$uploadprogress_received</uploaded></upload>';
upload_progress_template done '<upload><state>done</state></upload>';
upload_progress_template error '<upload><state>error</state><code>$uploadprogress_status</code></upload>';
JSONP 响应示例:
upload_progress_template starting "$uploadprogress_callback({ \"state\" : \"starting\"});";
upload_progress_template error "$uploadprogress_callback({ \"state\" : \"error\", \"status\" : $uploadprogress_status });";
upload_progress_template done "$uploadprogress_callback({ \"state\" : \"done\"});";
upload_progress_template uploading "$uploadprogress_callback({ \"state\" : \"uploading\", \"received\" : $uploadprogress_received, \"size\" : $uploadprogress_length });";
配置示例
http {
# 在名为 'proxied' 的区域下保留 1MB 以跟踪上传
upload_progress proxied 1m;
server {
listen 127.0.0.1 default;
server_name _ *;
root /path/to/root;
location / {
# 代理到上游服务器
proxy_pass http://127.0.0.1;
proxy_redirect default;
# 在 'proxied' 区域跟踪上传
# 在完成后记住连接 30 秒
track_uploads proxied 30s;
}
location ^~ /progress {
# 报告在 'proxied' 区域跟踪的上传
report_uploads proxied;
}
}
}
使用示例
基于 Lighttpd mod_uploadprogress 模块示例。
首先,我们需要一个上传表单:
<form id="upload" enctype="multipart/form-data"
action="/upload.php" method="post"
onsubmit="openProgressBar(); return true;">
<input type="hidden" name="MAX_FILE_SIZE" value="30000000" />
<input name="userfile" type="file" label="fileupload" />
<input type="submit" value="发送文件" />
</form>
以及一个进度条来可视化进度:
<div>
<div id="progress" style="width: 400px; border: 1px solid black">
<div id="progressbar"
style="width: 1px; background-color: black; border: 1px solid white">
</div>
</div>
<div id="tp">(进度)</div>
</div>
然后我们需要生成唯一标识符并在提交操作时启动上传。这也将启动 ajax 进度报告机制。
interval = null;
function openProgressBar() {
/* 生成随机进度 ID */
uuid = "";
for (i = 0; i < 32; i++) {
uuid += Math.floor(Math.random() * 16).toString(16);
}
/* 修补表单动作标签以包含进度 ID */
document.getElementById("upload").action="/upload.php?X-Progress-ID=" + uuid;
/* 每 1000 毫秒调用进度更新器 */
interval = window.setInterval(
function () {
fetch(uuid);
},
1000
);
}
function fetch(uuid) {
req = new XMLHttpRequest();
req.open("GET", "/progress", 1);
req.setRequestHeader("X-Progress-ID", uuid);
req.onreadystatechange = function () {
if (req.readyState == 4) {
if (req.status == 200) {
/* 简单的 JSON 解析器 */
var upload = eval(req.responseText);
document.getElementById('tp').innerHTML = upload.state;
/* 更改内进度条的宽度 */
if (upload.state == 'done' || upload.state == 'uploading') {
bar = document.getElementById('progressbar');
w = 400 * upload.received / upload.size;
bar.style.width = w + 'px';
}
/* 我们完成了,停止间隔 */
if (upload.state == 'done') {
window.clearTimeout(interval);
}
}
}
}
req.send(null);
}
伴随软件
该软件还可以与 Valery Kholodkov 的 Nginx 上传模块一起使用: http://www.grid.net.ru/nginx/upload.en.html
您还可以在客户端使用以下 JavaScript 库: http://drogomir.com/blog/2008/6/30/upload-progress-script-with-safari-support
请注意,在使用 jQuery AJAX 进行进度监控时,例如: https://github.com/drogus/jquery-upload-progress 您应该确保设置上传进度模板参数:
upload_progress_json_output
或
upload_progress_jsonp_output
具体取决于您的 jQuery AJAX dataType 设置。
GitHub
您可以在 nginx-module-upload-progress 的 GitHub 仓库 中找到有关此模块的其他配置提示和文档。