Detectando quando termina um download
Autor: Cristiano Pires | Publicado: 2012-01-24T10:43:00-02:00

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:

&lt;h:panelGrid&nbsp;columns="2"&gt;
&nbsp;&nbsp;&lt;h:graphicImage&nbsp;value="/pages/img/report.ico"&nbsp;/&gt;
&nbsp;&nbsp;&lt;a&nbsp;href="javascript:showDialog('#dialogTerminaisValidados');"&gt;lista&nbsp;de&nbsp;terminais&nbsp;ativos&lt;/a&gt;
&lt;/h:panelGrid&gt;



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:

&lt;div&nbsp;id="dialogTerminaisValidados"&nbsp;style="display:none;&nbsp;text-align:&nbsp;center;"&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;h:form&nbsp;id="frm_terminaisValidados"&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;p&gt;Esse&nbsp;processo&nbsp;poderá&nbsp;demorar&nbsp;alguns&nbsp;minutos.&nbsp;Tem&nbsp;certeza&nbsp;que&nbsp;deseja&nbsp;continuar?&lt;/p&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h:panelGrid&nbsp;columns="2"&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h:commandButton&nbsp;value="Ok"&nbsp;action="#{relatorioBean.geraListaTerminaisValidados}"
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;onclick="load('#dialogTerminaisValidados');"&nbsp;/&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;h:commandButton&nbsp;value="voltar"&nbsp;onclick="$('#dialogTerminaisValidados').dialog('close')"&nbsp;/&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/h:panelGrid&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;/h:form&gt;
&lt;/div&gt;


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()&nbsp;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.

&lt;script&nbsp;type='text/javascript'&gt;
&nbsp;&nbsp;var&nbsp;fileDownloadCheckTimer;

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

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

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

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


No lado servidor adicionaremos o cookie para enviar ao navegador

ExternalContext&nbsp;ectx&nbsp;=&nbsp;ctx.getExternalContext();
HttpServletResponse&nbsp;resp&nbsp;=&nbsp;(HttpServletResponse)&nbsp;ectx.getResponse();
resp.addCookie(new&nbsp;Cookie("success",&nbsp;"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&nbsp;static&nbsp;void&nbsp;downloadFile(String&nbsp;mimeType,&nbsp;FacesContext&nbsp;ctx,
&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;file)&nbsp;{
&nbsp;&nbsp;ExternalContext&nbsp;ectx&nbsp;=&nbsp;ctx.getExternalContext();
&nbsp;&nbsp;File&nbsp;f&nbsp;=&nbsp;new&nbsp;File(file);
&nbsp;&nbsp;HttpServletResponse&nbsp;resp&nbsp;=&nbsp;(HttpServletResponse)&nbsp;ectx.getResponse();
&nbsp;&nbsp;resp.addCookie(new&nbsp;Cookie("success",&nbsp;"success"));
&nbsp;&nbsp;resp.setHeader("Content-Disposition",&nbsp;"attachment;filename="&nbsp;+&nbsp;file
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+&nbsp;"");
&nbsp;&nbsp;resp.setContentLength((int)&nbsp;f.length());
&nbsp;&nbsp;resp.setContentType(mimeType);

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



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.