Pular para conteúdo

upload-progress: Módulo de rastreamento de progresso de upload do NGINX

Instalação

Você pode instalar este módulo em qualquer distribuição baseada em RHEL, incluindo, mas não se limitando a:

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

Ative o módulo adicionando o seguinte no início de /etc/nginx/nginx.conf:

load_module modules/ngx_http_uploadprogress_module.so;

Este documento descreve o nginx-module-upload-progress v0.9.4 lançado em 15 de março de 2025.


Introdução

nginx_uploadprogress_module é uma implementação de um sistema de progresso de upload, que monitora uploads POST RFC1867 enquanto são transmitidos para servidores upstream.

Ele funciona rastreando os uploads proxyados pelo NGINX para servidores upstream sem analisar o conteúdo enviado e oferece uma API web para relatar o progresso do upload em Javascript, JSON ou qualquer outro formato (com a ajuda de templates).

Funciona porque o NGINX atua como um acelerador de um servidor upstream, armazenando o conteúdo POST enviado em disco, antes de transmiti-lo para o servidor upstream. Cada solicitação de upload POST individual deve conter um identificador único de progresso.

Este módulo é Copyright (c) 2007-2012 Brice Figureau e está licenciado sob a licença BSD.

  • O código rbtree e shm_zone é baseado no módulo limit_zone do NGINX de Igor Sysoev.
  • O código do cabeçalho expire é baseado no módulo header_filter do NGINX de Igor Sysoev.

A ideia do JSON e a ideia do mecanismo são baseadas no Lighttpd mod_uploadprogress: http://blog.lighttpd.net/articles/2006/08/01/mod_uploadprogress-is-back

AVISO: quando compilado com --with-debug, este módulo produzirá um grande número de mensagens de log.

Mudanças Incompatíveis

v0.9.0:

JSONP agora é a saída padrão das sondas de progresso. Se você depender deste módulo para servir a saída java obsoleta, use:

upload_progress_java_output

na localização da sonda de progresso.

Configuração

Cada solicitação de upload deve ser atribuída a um identificador único. Este identificador único será usado para armazenar a solicitação e referenciá-la para relatar. Este identificador pode ser transmitido como um argumento GET ou como um cabeçalho HTTP cujo nome é X-Progress-ID.

upload_progress

Sintaxe upload_progress <zone_name> <zone_size>
Padrão nenhum
Contexto http

Esta diretiva habilita o módulo de progresso de upload e reserva <zone_size> bytes para <zone_name>, que será usado para armazenar as informações de rastreamento por conexão.

track_uploads

Sintaxe track_uploads <zone_name> <timeout>
Padrão nenhum
Contexto location

Esta diretiva habilita o rastreamento de uploads para a localização atual. Cada POST que chegar a esta localização registrará a solicitação no rastreador de progresso de upload <zone_name>. Como o NGINX ainda não suporta uploads RFC 1867, a localização deve ser uma localização proxy_pass ou fastcgi. O POST deve ter um parâmetro de consulta chamado X-Progress-ID (ou um cabeçalho HTTP com o mesmo nome) cujo valor é o identificador único usado para obter informações de progresso. Se o POST não tiver essas informações, o upload não será rastreado. As conexões rastreadas são mantidas por no máximo <timeout> segundos após terem sido finalizadas para poder fornecer informações úteis para as sondas de progresso de upload.

AVISO: esta diretiva deve ser a última diretiva da localização. Deve estar em uma localização proxy_pass ou fastcgi_pass.

report_uploads

Sintaxe report_uploads <zone_name>
Padrão nenhum
Contexto location

Esta diretiva permite que uma localização relate o progresso do upload que está sendo rastreado por track_uploads para <zone_name>. O documento retornado é um texto Javascript com os possíveis 4 resultados por padrão:

  • A solicitação de upload ainda não foi registrada ou é desconhecida:

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

  • A solicitação de upload foi finalizada:

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

  • A solicitação de upload gerou um erro HTTP:

    new Object({ 'state' : 'error', 'status' : <error code> })
    
    Um código de erro que pode ser útil para rastrear para o cliente é 413 (entidade da solicitação muito grande).

  • A solicitação de upload está em progresso:

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

É possível retornar JSON puro em vez deste javascript (veja upload_progress_json_output). Também é possível configurar completamente o formato da resposta com a diretiva upload_progress_template.

A solicitação HTTP para esta localização deve ter um parâmetro X-Progress-ID ou cabeçalho HTTP contendo um identificador único válido de um upload em progresso.

upload_progress_content_type

Sintaxe upload_progress_content_type <content_type>
Padrão text/javascript
Contexto location

Esta diretiva permite alterar o tipo de conteúdo da resposta da sonda de progresso de upload.

upload_progress_header

Sintaxe upload_progress_header <progress-id>
Padrão X-Progress-ID
Contexto location

Esta diretiva permite alterar o nome do cabeçalho do ID de progresso.

upload_progress_jsonp_parameter

Sintaxe upload_progress_jsonp_parameter <callback_parameter>
Padrão callback
Contexto location

Esta diretiva permite alterar o nome do parâmetro GET com o nome do callback jsonp.

upload_progress_java_output

Sintaxe upload_progress_java_output
Padrão N/A
Contexto location

Esta diretiva configura tudo para ser gerado como código javascript compatível com eval().

upload_progress_json_output

Sintaxe upload_progress_json_output
Padrão N/A
Contexto location

Esta diretiva configura tudo para ser gerado como JSON puro.

upload_progress_jsonp_output

Sintaxe upload_progress_jsonp_output
Padrão N/A
Contexto location

Esta diretiva configura tudo para ser gerado como JSONP (como saída JSON, mas com callback).

upload_progress_template

Sintaxe upload_progress_template <state> <template>
Padrão nenhum
Contexto location

Esta diretiva pode ser usada para instalar um template de resposta de progresso. A lista disponível de estados é:

  • starting
  • uploading
  • error
  • done

O NGINX substituirá o valor das seguintes variáveis por seus respectivos valores para o upload:

  • $uploadprogress_length: tamanho total do upload
  • $uploadprogress_received: o que o servidor recebeu até agora
  • $uploadprogress_status: código de erro em caso de erro HTTP
  • $uploadprogress_callback: nome do callback jsonp se fornecido como um parâmetro de consulta GET com o nome 'callback'

Por exemplo, para retornar XML (em vez do Javascript ou JSON padrão):

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

Exemplo de resposta 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 });";

Exemplo de Configuração

http {
    # reserve 1MB sob o nome 'proxied' para rastrear uploads
    upload_progress proxied 1m;

    server {
        listen       127.0.0.1 default;
        server_name  _ *;

        root /path/to/root;

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

            # rastrear uploads na zona 'proxied'
            # lembrar conexões por 30s após terem terminado
            track_uploads proxied 30s;
        }

        location ^~ /progress {
            # relatar uploads rastreados na zona 'proxied'
            report_uploads proxied;
        }
    }
}

Exemplo de Uso

Baseado no exemplo do módulo Lighttpd mod_uploadprogress.

Primeiro, precisamos de um formulário de upload:

<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 Arquivo" />
</form>

E uma barra de progresso para visualizar o progresso:

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

Então, precisamos gerar o Identificador Único e iniciar o upload na ação de envio. Isso também iniciará o mecanismo de relatório de progresso ajax.

interval = null;

function openProgressBar() {
  /* gerar um progress-id aleatório */
  uuid = "";
  for (i = 0; i < 32; i++) {
    uuid += Math.floor(Math.random() * 16).toString(16);
  }
  /* atualizar a tag de ação do formulário para incluir o progress-id */
  document.getElementById("upload").action="/upload.php?X-Progress-ID=" + uuid;

  /* chamar o atualizador de progresso a 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 simples */
        var upload = eval(req.responseText);

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

        /* mudar a largura da barra de progresso interna */
        if (upload.state == 'done' || upload.state == 'uploading') {
          bar = document.getElementById('progressbar');
          w = 400 * upload.received / upload.size;
          bar.style.width = w + 'px';
        }
        /* terminamos, pare o intervalo */
        if (upload.state == 'done') {
          window.clearTimeout(interval);
        }
      }
    }
  }
  req.send(null);
}

Software Complementar

Este software também pode funcionar com o Módulo de Upload do NGINX de Valery Kholodkov: http://www.grid.net.ru/nginx/upload.en.html

Você também pode usar as seguintes bibliotecas javascript do lado do cliente: http://drogomir.com/blog/2008/6/30/upload-progress-script-with-safari-support

Observe que ao usar jQuery AJAX para monitoramento de progresso, como: https://github.com/drogus/jquery-upload-progress, você deve ter certeza de definir um parâmetro de template de upload_progress:

upload_progress_json_output

ou

upload_progress_jsonp_output

dependendo da configuração de dataType do seu jQuery AJAX.

GitHub

Você pode encontrar dicas adicionais de configuração e documentação para este módulo no repositório do GitHub para nginx-module-upload-progress.