Excel Download pelo Browser com Jsf e Apache POI

Boa noite amigos,

Estou com um problema numa funcionalidade para exportar dados em excel, no caso no browser para download, a partir do framework POI que estou usando pra gerar o arquivo a partir de uma lista.

Usando h:commandLink e ajax

<h:commandLink id="cl-exportar" immediate="true" >
	<img src="#{facesContext.externalContext.request.contextPath}/resources/design/images/icone_excel.png"/>
	<f:ajax event="click" execute="@all" onevent="onAjaxClickEvent" render="@all" immediate="true" listener="#{myBean.exportar()}" />
</h:commandLink>

E no javascript importado eu tenho a function do onevent acima, que serve para mostrar um gif de carregando assim que inicia a exportação e esconder quando o arquivo estiver no browser para download:

function onAjaxClickEvent(data) {
	switch (data.status) {
		case "begin":
			showGifLoading();
			break;
		case "success":			
			hideGifLoading();
			break;
	}
}

Meu Bean extende de uma classe geral do sistema que retorna o response

protected HttpServletResponse getResponse(){
	return this.getExternalContext() != null ? (HttpServletResponse) this.getExternalContext().getResponse() : null;
}

E meu código de gerar e exportar o arquivo:

public void exportar() throws ServiceLayerException, ParseException, IOException {
	long start = System.currentTimeMillis();
	HSSFWorkbook workbook = new HSSFWorkbook();
	HSSFSheet sheet = workbook.createSheet("Livros");
	
	int rownum  = 0;
	
	Row header = sheet.createRow(rownum++);
	header.createCell(0).setCellValue("Nome");
	header.createCell(1).setCellValue("Autor");
	header.createCell(2).setCellValue("Genero");
		
	for(Livros livro: this.lista) {
		 Row row = sheet.createRow(rownum++);
		 row.createCell(0, CellType.STRING).setCellValue(livro.getNome());
		 row.createCell(1, CellType.STRING).setCellValue(livro.getAutor());
		 row.createCell(2, CellType.STRING).setCellValue(livro.getGenero));
	}
	
	try {
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		
		byte[] arquivoBytes = bos.toByteArray();
		
		super.getResponse().setContentType("application/force-download");
		super.getResponse().setContentLength(arquivoBytes.length);
		super.getResponse().setHeader("Content-Transfer-Encoding", "binary");
		super.getResponse().setHeader("Content-disposition", "attachment; filename=livros.xsl");
		super.getResponse().setCharacterEncoding("UTF-8");
		ServletOutputStream ouputStream = super.getResponse().getOutputStream();
		ouputStream.write(arquivoBytes, 0, arquivoBytes.length);
		workbook.write(ouputStream);
		ouputStream.flush();
		ouputStream.close();
		super.getCurrentFacesContext().renderResponse();
		super.getCurrentFacesContext().responseComplete();
	} finally {
		workbook.close();
		log.info("Tempo para gerar a planilha: " + ((System.currentTimeMillis() - start)/1000) + " segundos.");
	}
}

O arquivo nunca é exportado, na verdade o gif de loading é carregado e nunca escondido
E PIOR!!! O log do finally é chamado :confused:
Se alguém puder me dar um help, estou há semanas tentando fazer isso funcionar

Como teste, tente remover a chamada do método do ajax e coloque como action do botão.

@Lucas_Camara, eu já fiz esse teste e sim funciona, mas eu preciso do onevent=“onAjaxClickEvent” do ajax pra gerar o gif de loading no inicio da exportação e quando finalizar.
Eu queria entender como fazer funcionar com ajax e pq funciona sem ele igual vc mencionou.

Para funcionar via ajax, vc deve retornar os bytes numa requisição e fazer o tratamento via javascript, usando um Blob.

Já precisei fazer isso e funcionou sem problemas. A ideia é vc retornar o arquivo do servidor no formato base64 e, no javascript, vc fazer o tratamento desse conteúdo obtendo um Blob e forçar o download já no lado cliente (browser). Bem capaz que vc precisará usar um callback no componente ajax (um oncomplete que irá receber o retorno do método chamado no managedbean).

Esse link ensina como fazer: https://medium.com/@riccardopolacci/download-file-in-javascript-from-bytea-6a0c5bb3bbdb.

Vlw @Lucas_Camara, vou dar uma olhada e testar! Obrigado msm!
Só mais uma coisa, o meu Ajax não tem a opção de oncomplete, tbm é uma coisa que eu queria aprender a fazer

Ah sim, f:ajax é jsf2 neh. Então, pela documentação, parece que é o onevent (que vc já está usando). A função definida no onevent irá receber o retorno do método chamado no managedBean.