跳转至

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-IDHTTP 头传输。

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_passfastcgi 位置。 POST 必须 有一个名为 X-Progress-ID 的查询参数(或同名的 HTTP 头),其值是用于获取进度信息的唯一标识符。如果 POST 没有此信息,则不会跟踪上传。 跟踪的连接在完成后最多保留 <timeout> 秒,以便能够为上传进度探测提供有用的信息。

警告: 此指令必须是位置的最后一条指令。它必须位于 proxy_passfastcgi_pass 位置中。

report_uploads

语法 report_uploads <zone_name>
默认
上下文 location

该指令允许位置报告由 track_uploads 跟踪的 <zone_name> 的上传进度。 返回的文档是一个 Javascript 文本,默认可能有 4 种结果:

  • 上传请求尚未注册或未知:

    new Object({ 'state' : 'starting' })
    

  • 上传请求已结束:

    new Object({ 'state' : 'done' })
    

  • 上传请求生成了 HTTP 错误:

    new Object({ 'state' : 'error', 'status' : <error code> })
    
    一个可以用于跟踪客户端的错误代码是 413(请求实体过大)。

  • 上传请求正在进行中:

    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

该指令可用于安装进度响应模板。 可用的状态列表包括:

  • starting
  • uploading
  • error
  • done

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">
      &nbsp;
    </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 仓库 中找到有关此模块的其他配置提示和文档。