Saltar a contenido

upload-progress: Módulo de seguimiento del progreso de carga de NGINX

Instalación

Puedes instalar este módulo en cualquier distribución basada en RHEL, incluyendo, pero no limitado a:

  • RedHat Enterprise Linux 7, 8, 9 y 10
  • CentOS 7, 8, 9
  • AlmaLinux 8, 9
  • Rocky Linux 8, 9
  • Amazon Linux 2 y 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

Habilita el módulo añadiendo lo siguiente en la parte superior de /etc/nginx/nginx.conf:

load_module modules/ngx_http_uploadprogress_module.so;

Este documento describe nginx-module-upload-progress v0.9.4 lanzado el 15 de marzo de 2025.


Introducción

nginx_uploadprogress_module es una implementación de un sistema de progreso de carga que monitorea las cargas POST RFC1867 a medida que se transmiten a servidores upstream.

Funciona rastreando las cargas proxyadas por Nginx a servidores upstream sin analizar el contenido cargado y ofrece una API web para informar el progreso de carga en Javascript, JSON o cualquier otro formato (con la ayuda de plantillas).

Funciona porque Nginx actúa como un acelerador de un servidor upstream, almacenando el contenido POST cargado en disco, antes de transmitirlo al servidor upstream. Cada solicitud de carga POST individual debe contener un identificador único de progreso.

Este módulo es Copyright (c) 2007-2012 Brice Figureau, y está licenciado bajo la licencia BSD.

  • El código de rbtree y shm_zone se basa en el módulo limit_zone de Igor Sysoev para Nginx.
  • El código del encabezado expire se basa en el módulo header_filter de Igor Sysoev para Nginx.

La idea de JSON y la idea del mecanismo se basan en Lighttpd mod_uploadprogress: http://blog.lighttpd.net/articles/2006/08/01/mod_uploadprogress-is-back

ADVERTENCIA: cuando se compila con --with-debug, este módulo producirá un alto número de mensajes de registro.

Cambios Incompatibles

v0.9.0:

JSONP es ahora la salida predeterminada de las sondas de progreso. Si dependes de este módulo para servir la salida java obsoleta usa:

upload_progress_java_output

en la ubicación de la sonda de progreso.

Configuración

Cada solicitud de carga debe asignarse un identificador único. Este identificador único se utilizará para almacenar la solicitud y hacer referencia a ella para informar. Este identificador puede transmitirse ya sea como un argumento GET o como un encabezado HTTP cuyo nombre es X-Progress-ID.

upload_progress

Sintaxis upload_progress <zone_name> <zone_size>
Predeterminado ninguno
Contexto http

Esta directiva habilita el módulo de progreso de carga y reserva <zone_size> bytes para el <zone_name> que se utilizará para almacenar la información de seguimiento por conexión.

track_uploads

Sintaxis track_uploads <zone_name> <timeout>
Predeterminado ninguno
Contexto location

Esta directiva habilita el seguimiento de cargas para la ubicación actual. Cada POST que llegue a esta ubicación registrará la solicitud en el rastreador de progreso de carga <zone_name>. Dado que Nginx aún no soporta la carga RFC 1867, la ubicación debe ser una ubicación proxy_pass o fastcgi. El POST debe tener un parámetro de consulta llamado X-Progress-ID (o un encabezado HTTP con el mismo nombre) cuyo valor es el identificador único utilizado para obtener información de progreso. Si el POST no tiene tal información, la carga no será rastreada. Las conexiones rastreadas se mantienen como máximo <timeout> segundos después de que han terminado para poder servir información útil a las sondas de progreso de carga.

ADVERTENCIA: esta directiva debe ser la última directiva de la ubicación. Debe estar en una ubicación proxy_pass o fastcgi_pass.

report_uploads

Sintaxis report_uploads <zone_name>
Predeterminado ninguno
Contexto location

Esta directiva permite a una ubicación informar el progreso de carga que se rastrea mediante track_uploads para <zone_name>. El documento devuelto es un texto Javascript con los posibles 4 resultados por defecto:

  • La solicitud de carga aún no ha sido registrada o es desconocida:

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

  • La solicitud de carga ha terminado:

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

  • La solicitud de carga generó un error HTTP:

    new Object({ 'state' : 'error', 'status' : <error code> })
    
    Un código de error que puede ser útil para rastrear para el cliente es 413 (entidad de solicitud demasiado grande).

  • La solicitud de carga está en progreso:

    new Object({ 'state' : 'uploading', 'received' : <size_received>, 'size' : <total_size>})
    

Es posible devolver JSON puro en lugar de este javascript (ver upload_progress_json_output). También es posible configurar completamente el formato de respuesta con la directiva upload_progress_template.

La solicitud HTTP a esta ubicación debe tener un parámetro X-Progress-ID o un encabezado HTTP que contenga un identificador único válido de una carga en progreso.

upload_progress_content_type

Sintaxis upload_progress_content_type <content_type>
Predeterminado text/javascript
Contexto location

Esta directiva permite cambiar el tipo de contenido de la respuesta de la sonda de progreso de carga.

upload_progress_header

Sintaxis upload_progress_header <progress-id>
Predeterminado X-Progress-ID
Contexto location

Esta directiva permite cambiar el nombre del encabezado del ID de progreso.

upload_progress_jsonp_parameter

Sintaxis upload_progress_jsonp_parameter <callback_parameter>
Predeterminado callback
Contexto location

Esta directiva permite cambiar el nombre del parámetro GET con el nombre de la función de devolución de llamada jsonp.

upload_progress_java_output

Sintaxis upload_progress_java_output
Predeterminado N/A
Contexto location

Esta directiva establece todo para que se produzca como código javascript compatible con eval().

upload_progress_json_output

Sintaxis upload_progress_json_output
Predeterminado N/A
Contexto location

Esta directiva establece todo para que se produzca como JSON puro.

upload_progress_jsonp_output

Sintaxis upload_progress_jsonp_output
Predeterminado N/A
Contexto location

Esta directiva establece todo para que se produzca como JSONP (como salida JSON, pero con devolución de llamada).

upload_progress_template

Sintaxis upload_progress_template <state> <template>
Predeterminado ninguno
Contexto location

Esta directiva se puede utilizar para instalar una plantilla de respuesta de progreso. La lista disponible de estados es:

  • starting
  • uploading
  • error
  • done

Nginx reemplazará el valor de las siguientes variables con su respectivo valor para la carga:

  • $uploadprogress_length: tamaño total de la carga
  • $uploadprogress_received: lo que el servidor ha recibido hasta ahora
  • $uploadprogress_status: código de error en caso de error HTTP
  • $uploadprogress_callback: nombre de la devolución de llamada jsonp si se proporciona como un parámetro de consulta GET con el nombre 'callback'

Por ejemplo, para devolver XML (en lugar del Javascript o JSON predeterminado):

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>';

Ejemplo de respuesta 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 });";

Ejemplo de Configuración

http {
    # reservar 1MB bajo el nombre 'proxied' para rastrear cargas
    upload_progress proxied 1m;

    server {
        listen       127.0.0.1 default;
        server_name  _ *;

        root /path/to/root;

        location / {
            # proxy a servidor upstream
            proxy_pass http://127.0.0.1;
            proxy_redirect default;

            # rastrear cargas en la zona 'proxied'
            # recordar conexiones durante 30s después de que terminen
            track_uploads proxied 30s;
        }

        location ^~ /progress {
            # informar cargas rastreadas en la zona 'proxied'
            report_uploads proxied;
        }
    }
}

Ejemplo de Uso

Basado en el ejemplo del módulo Lighttpd mod_uploadprogress.

Primero necesitamos un formulario de carga:

<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="Enviar Archivo" />
</form>

Y una barra de progreso para visualizar el progreso:

<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">(progreso)</div>
</div>

Luego necesitamos generar el Identificador Único y lanzar la carga en la acción de envío. Esto también iniciará el mecanismo de informe de progreso ajax.

interval = null;

function openProgressBar() {
  /* generar un progress-id aleatorio */
  uuid = "";
  for (i = 0; i < 32; i++) {
    uuid += Math.floor(Math.random() * 16).toString(16);
  }
  /* parchear la etiqueta de acción del formulario para incluir el progress-id */
  document.getElementById("upload").action="/upload.php?X-Progress-ID=" + uuid;

  /* llamar al actualizador de progreso cada 1000ms */
  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) {
        /* parser JSON de pobre */
        var upload = eval(req.responseText);

        document.getElementById('tp').innerHTML = upload.state;

        /* cambiar el ancho de la barra de progreso interna */
        if (upload.state == 'done' || upload.state == 'uploading') {
          bar = document.getElementById('progressbar');
          w = 400 * upload.received / upload.size;
          bar.style.width = w + 'px';
        }
        /* hemos terminado, detener el intervalo */
        if (upload.state == 'done') {
          window.clearTimeout(interval);
        }
      }
    }
  }
  req.send(null);
}

Software Complementario

Este software también puede funcionar con el Módulo de Carga de Nginx de Valery Kholodkov: http://www.grid.net.ru/nginx/upload.en.html

También puedes usar las siguientes bibliotecas de javascript del lado del cliente: http://drogomir.com/blog/2008/6/30/upload-progress-script-with-safari-support

Ten en cuenta que al usar jQuery AJAX para el monitoreo de progreso, como: https://github.com/drogus/jquery-upload-progress debes asegurarte de establecer un parámetro de plantilla upload_progress:

upload_progress_json_output

o

upload_progress_jsonp_output

dependiendo de tu configuración de dataType de jQuery AJAX.

GitHub

Puedes encontrar consejos de configuración adicionales y documentación para este módulo en el repositorio de GitHub para nginx-module-upload-progress.