Перейти к содержанию

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, выпущенный 15 марта 2025 года.


Введение

nginx_uploadprogress_module — это реализация системы отслеживания прогресса загрузки, которая мониторит загрузки POST по RFC1867 по мере их передачи на серверы upstream.

Он работает, отслеживая загрузки, проксируемые Nginx на серверы upstream, не анализируя загруженное содержимое, и предлагает веб-API для отчетов о прогрессе загрузки в Javascript, JSON или любом другом формате (с помощью шаблонов).

Это работает, потому что Nginx выступает в качестве ускорителя для сервера upstream, храня загруженное содержимое POST на диске перед передачей его на сервер upstream. Каждый отдельный запрос загрузки POST должен содержать уникальный идентификатор прогресса.

Этот модуль является объектом авторского права (c) 2007-2012 Brice Figureau и лицензирован по лицензии BSD.

  • Код rbtree и shm_zone основан на модуле Nginx limit_zone Игоря Сысоева.
  • Код заголовка expire основан на модуле Nginx header_filter Игоря Сысоева.

Идея 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, либо как HTTP-заголовок с именем X-Progress-ID.

upload_progress

Синтаксис upload_progress <zone_name> <zone_size>
По умолчанию нет
Контекст http

Эта директива включает модуль отслеживания прогресса загрузки и резервирует <zone_size> байт для <zone_name>, который будет использоваться для хранения информации об отслеживании на соединение.

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-ошибку:

    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

Эта директива позволяет изменить имя заголовка идентификатора прогресса.

upload_progress_jsonp_parameter

Синтаксис upload_progress_jsonp_parameter <callback_parameter>
По умолчанию callback
Контекст location

Эта директива позволяет изменить имя параметра GET с именем обратного вызова jsonp.

upload_progress_java_output

Синтаксис upload_progress_java_output
По умолчанию N/A
Контекст location

Эта директива устанавливает все для вывода как совместимый код javascript для eval().

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: имя обратного вызова jsonp, если предоставлено как параметр GET с именем 'callback'

Например, чтобы вернуть 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 {
    # резервируем 1MB под именем 'proxied' для отслеживания загрузок
    upload_progress proxied 1m;

    server {
        listen       127.0.0.1 default;
        server_name  _ *;

        root /path/to/root;

        location / {
            # проксируем на upstream сервер
            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="Send File" />
</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">(progress)</div>
</div>

Затем нам нужно сгенерировать уникальный идентификатор и запустить загрузку при отправке формы. Это также запустит механизм отчетов о прогрессе ajax.

interval = null;

function openProgressBar() {
  /* генерируем случайный progress-id */
  uuid = "";
  for (i = 0; i < 32; i++) {
    uuid += Math.floor(Math.random() * 16).toString(16);
  }
  /* изменяем тег action формы, чтобы включить progress-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);
}

Сопутствующее программное обеспечение

Это программное обеспечение также может работать с модулем загрузки 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:

upload_progress_json_output

или

upload_progress_jsonp_output

в зависимости от настройки dataType вашего jQuery AJAX.

GitHub

Вы можете найти дополнительные советы по конфигурации и документацию для этого модуля в репозитории GitHub для nginx-module-upload-progress.