Detectando quando termina um download

Numa aplicação web que fiz às vezes é necessário criar documentos CSV com base em alguma entrada do usuário.

O problema é que o arquivo gerado ficou grande, então o download era demorado. Eu queria mostrar uma tela para o usuário dizendo quando o download iniciou e retirar essa tela quando o download terminar.

O problema: como detectar quando acabou o download e atualizar isso no navegador?

Existem algumas formas de fazer isso, porém eu queria algo simples, rápido que não fosse necessário escrever arquivo, ou algum mecanismo de cache complicado.
Achei essa solução que acredito que pode ser útil pra muita gente.

Dependências:
Java Server Faces
Jquery
Jquery cookies

Abaixo, no lado cliente:
<h:panelGrid columns="2">
  <h:graphicImage value="/pages/img/report.ico" />
  <a href="javascript:showDialog('#dialogTerminaisValidados');">lista de terminais ativos</a>
</h:panelGrid>

Agora, o div dialogTerminaisValidados, onde será exibido uma caixa de diálogo informando o usuário que o download poderá demorar alguns minutos e solicitando sua confirmação de download:
<div id="dialogTerminaisValidados" style="display:none; text-align: center;">
    <h:form id="frm_terminaisValidados">
      <p>Esse processo poderá demorar alguns minutos. Tem certeza que deseja continuar?</p>
      <h:panelGrid columns="2">
        <h:commandButton value="Ok" action="#{relatorioBean.geraListaTerminaisValidados}"
          onclick="load('#dialogTerminaisValidados');" />
        <h:commandButton value="voltar" onclick="$('#dialogTerminaisValidados').dialog('close')" />
      </h:panelGrid>
    </h:form>
</div>

O javascript onde trataremos as caixas de diálogo e o processo de download.

Na primeira função 'load(idHide)' iremos eliminar a caixa de confirmação de download, onde o usuário confirma e em seguida iremos exibir uma caixa de informação de processo do download. Essa deve ficar ativa até a conclusão do download.



Logo é chamada a função checkDownload(), que vai verificar se existe um cookie chamado 'success', em seguida pegar o seu valor e finalizar a caixa de informação de processo do download. Observe que enquando não é finalizado, a função fica tentando encontrar o cookie de segundo em segundo.


Após a chegada do cookie, é chamado a função finishDownload() que elimina a caixa de diálogo do processo de download e finaliza o processo de procura do cookie e ainda elimina o cookie. Caso haja uma nova solicitação de download será gerado um novo cookie no servidor.
<script type='text/javascript'>
  var fileDownloadCheckTimer;

  function load(idHide){
  if (idHide != null) {
    $(idHide).dialog('close');
    $(idHide).hide();
    checkDownload();
  }
    $('#wait').show().dialog({
      height:80,
      width: 200,
      closeOnEscape: false,
      resizable: false,
      disabled: true,
      title: 'Processando...', 
      modal:true
      })
  }

  function checkDownload() {
    fileDownloadCheckTimer = window.setInterval(function () {
      var cookieValue = $.cookies.get('success');
      if (cookieValue == 'success')
          finishDownload();
    }1000);
  }

  function finishDownload() {
    $('#wait').dialog('close');
    $('#wait').hide();
    window.clearInterval(fileDownloadCheckTimer);
    $.cookies.del('success'); 
  }

  function showDialog(idShow){
    $(idShow).show().dialog({
      height:130,
      width: 350,
      closeOnEscape: false,
      resizable: false,
      disabled: true,
      dialogClass: 'alert',
      title: 'Atenção!', 
      modal:true
      })
  }
</script>

No lado servidor adicionaremos o cookie para enviar ao navegador
ExternalContext ectx = ctx.getExternalContext();
HttpServletResponse resp = (HttpServletResponseectx.getResponse();
resp.addCookie(new Cookie("success""success"));

Incluí um cookie chamado 'success' que será enviado ao navegador juntamente com o processo de download concluído.

Abaixo deixo o método que eu utilizo para processar o download. Eu envio o tipo de arquivo, no meu caso 'text/plain' por que é do tipo 'CSV', o contexto do jsf e o nome do arquivo.
public static void downloadFile(String mimeType, FacesContext ctx,
    String file) {
  ExternalContext ectx = ctx.getExternalContext();
  File f = new File(file);
  HttpServletResponse resp = (HttpServletResponseectx.getResponse();
  resp.addCookie(new Cookie("success""success"));
  resp.setHeader("Content-Disposition""attachment;filename=" + file
      "");
  resp.setContentLength((intf.length());
  resp.setContentType(mimeType);

  try {
    FileInputStream in = new FileInputStream(f);
    OutputStream out = resp.getOutputStream();
    byte[] buf = new byte[(intf.length()];
    int count;
    while ((count = in.read(buf)) >= 0) {
      out.write(buf, 0, count);
    }
    in.close();
    out.flush();
    out.close();
    ctx.responseComplete();
  catch (IOException ioe) {
    System.err.println(ioe.getMessage());
  }
}

Quando finalizar o processamento desse método o download será iniciado na tela do navegador e a mensagem que estava processando irá desaparecer. Achei que ficou muito bom no meu caso.

É isso!
[]'s.

Comentários

Postagens mais visitadas deste blog

Utilizando um pool de conexões com hibernate

Gravando dados de um arquivo CSV no Oracle utlizando Python e a biblioteca cx_Oracle

Popup em JSF sem Javascript