jsf - 如何修复 Primefaces 文件下载在仅第二次起作用的命令按钮内?
问题描述
我正在开发一个新的功能,我需要它来下载从数据库查询生成的 CSV 文件。我将 Primefaces 3.5 与 JSF 2.1 和 JBoss 6.1 一起使用。IDE 是 Eclipse Oxygen。
主要是,用户可以下载与某个月/年日期相关的员工文件。但是,当我单击下载按钮时,它第一次不起作用。奇怪的是,当再次单击该按钮时,它工作正常!
该功能有两个屏幕:
第一:在这个屏幕中有一个表单,用户可以通过两个按钮访问第二个屏幕。任何一个按钮都会打开第二个屏幕(由于一些未来的功能,有两个按钮,所以现在不要介意)。按钮使用 action 标签上定义的方法的字符串返回重定向流程。
在第二个屏幕中,用户可以在 selectOneMenu 框中选择一个日期并单击该按钮。当用户这样做时,将生成 csv 文件。
但是,如前所述,第一次单击时,会联系服务器(消息显示在浏览器栏中),但没有下载文件。在用户第二次单击该按钮时,文件被下载。
不会抛出异常。
我已经尝试使用 h: 标签而不是 p: 更改代码,将按钮放在 panelGrid 之外并在互联网上搜索但没有成功。
一些代码更改使文件在第二个屏幕加载期间生成,然后单击下载按钮。但这是不可取的。
第一个屏幕代码:arquivoDiariasDFI.xhtml
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:c="http://java.sun.com/jsp/jstl/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui">
<h:head>
</h:head>
<h:body >
<ui:composition template="/layout/common.xhtml">
<ui:param name="mbean" value="#{arquivoDiariasDFIManagedBean}" />
<ui:param name="entity" value="Arquivos de Diarias - DFI" />
<ui:define name="content">
<h:form id="tela2" acceptcharset="ISO-8859-1">
<h:outputScript library="javascript" name="locale-primefaces.js" />
<p:messages id="messages" showDetail="false" autoUpdate="true" closable="true" globalOnly="false" />
<div class="lblFuncionalidade">#{entity}</div>
<div>
<p></p>
<p:panelGrid border="1" width="100%" >
<p:row style="border:0px; width:100%" columns="3" >
<p:column style="border:0px; font-weight:bold; width:10%" > Mes Base: </p:column>
<p:column style="border:0px; font-weight:bold; width:50%">
<p:selectOneMenu id="smMesBase" filter="true" value="#{mbean.arquivoDiariasDFI.dataBase}"
required="true" requiredMessage="Campo Obrigatorio: Mes Base" disabled="#{mbean.desabDataBase}" >
<f:selectItem itemLabel="Selecione o mes base" />
<f:selectItems value="#{mbean.datasBases}" var="dtBase" itemValue="#{dtBase}"
itemLabel="#{dtBase}" >
</f:selectItems>
</p:selectOneMenu>
</p:column>
<p:column style="border:0px;" >
<h:commandButton value="Gerar arquivo de integrantes" id="botaoIntegrantes"
action="#{mbean.botaoArquivoFuncionarios()}"
onclick="PrimeFaces.monitorDownload(start, stop);" >
<p:fileDownload value="#{mbean.arquivo}"/>
</h:commandButton>
</p:column>
</p:row>
</p:panelGrid>
<p></p>
<!-- -->
</div>
<p:ajaxStatus onstart="statusDialog.show();" oncomplete="statusDialog.hide();" />
<p:dialog modal="true" widgetVar="statusDialog" showHeader="false"
draggable="false" closable="false" resizable="false">
<p:graphicImage value="../imagens/ajax-loader.gif" />
</p:dialog>
</h:form>
</ui:define>
</ui:composition>
</h:body>
</html>
第二屏代码 arquivoDiariasDFI_detalhe.xhtml
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:c="http://java.sun.com/jsp/jstl/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui">
<h:head>
</h:head>
<h:body >
<ui:composition template="/layout/common.xhtml">
<ui:param name="mbean" value="#{arquivoDiariasDFIManagedBean}" />
<ui:param name="entity" value="Arquivos de Diarias - DFI" />
<ui:define name="content">
<h:form id="tela2" acceptcharset="ISO-8859-1">
<h:outputScript library="javascript" name="locale-primefaces.js" />
<p:messages id="messages" showDetail="false" autoUpdate="true" closable="true" globalOnly="false" />
<div class="lblFuncionalidade">#{entity}</div>
<div>
<p></p>
<p:panelGrid border="1" width="100%" >
<p:row style="border:0px; width:100%" columns="3" >
<p:column style="border:0px; font-weight:bold; width:10%" > Mes Base: </p:column>
<p:column style="border:0px; font-weight:bold; width:50%">
<p:selectOneMenu id="smMesBase" filter="true" value="#{mbean.arquivoDiariasDFI.dataBase}"
required="true" requiredMessage="Campo Obrigatorio: Mes Base" disabled="#{mbean.desabDataBase}" >
<f:selectItem itemLabel="Selecione o mes base" />
<f:selectItems value="#{mbean.datasBases}" var="dtBase" itemValue="#{dtBase}"
itemLabel="#{dtBase}" >
</f:selectItems>
</p:selectOneMenu>
</p:column>
<p:column style="border:0px;" >
<h:commandButton value="Gerar arquivo de integrantes" id="botaoIntegrantes"
action="#{mbean.botaoArquivoFuncionarios()}"
onclick="PrimeFaces.monitorDownload(start, stop);" >
<p:fileDownload value="#{mbean.arquivo}"/>
</h:commandButton>
</p:column>
</p:row>
</p:panelGrid>
<p></p>
<!-- -->
</div>
<p:ajaxStatus onstart="statusDialog.show();" oncomplete="statusDialog.hide();" />
<p:dialog modal="true" widgetVar="statusDialog" showHeader="false"
draggable="false" closable="false" resizable="false">
<p:graphicImage value="../imagens/ajax-loader.gif" />
</p:dialog>
</h:form>
</ui:define>
</ui:composition>
</h:body>
</html>
托管 bean(抑制代码)。
package mppr.srh.dominio.bean;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import org.primefaces.model.DefaultStreamedContent;
import org.primefaces.model.StreamedContent;
import mppr.srh.manager.ArquivoDiariasDFIManagerLocal;
import mppr.srh.manager.FuncionarioManagerLocal;
import mppr.srh.model.ArquivoDiariasDFI;
import mppr.srh.model.Funcionario;
@ManagedBean
@SessionScoped
public class ArquivoDiariasDFIManagedBean {
private static final long serialVersionUID = 1L;
private String status = "tela1"; //Controle de tela
private Boolean desabDataBase = false; //Controle de botão Mes Base
private List < Funcionario > funcionarios;
@EJB
FuncionarioManagerLocal funcionarioManagerLocal;
@EJB
private ArquivoDiariasDFIManagerLocal svc;
private List < ArquivoDiariasDFI > listArquivoDiariasDFI;
private ArquivoDiariasDFI arquivoDiariasDFI;
private List < String > datasBases;
private StreamedContent arquivo;
String nomeArq;
//Métodos "padrão"
@PostConstruct
public void construct(){
this.setStatus("tela1");
listArquivoDiariasDFI = svc.retornaTodasAsDiarias();
}
public List<String> getDatasBases() {
return datasBases;
}
public void setDatasBases(List<String> datasBases) {
this.datasBases = datasBases;
}
public String getNomeArq() {
return nomeArq;
}
public void setNomeArq(String nomeArq) {
this.nomeArq = nomeArq;
}
public Boolean getDesabDataBase() {
return desabDataBase;
}
public void setDesabDataBase(Boolean desabDataBase) {
this.desabDataBase = desabDataBase;
}
public List<ArquivoDiariasDFI> getListArquivoDiariasDFI() {
return listArquivoDiariasDFI;
}
public void setListArquivoDiariasDFI(List<ArquivoDiariasDFI> listArquivoDiariasDFI) {
this.listArquivoDiariasDFI = listArquivoDiariasDFI;
}
public ArquivoDiariasDFI getArquivoDiariasDFI() {
return arquivoDiariasDFI;
}
public void setArquivoDiariasDFI(ArquivoDiariasDFI arquivoDiariasDFI) {
this.arquivoDiariasDFI = arquivoDiariasDFI;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public List<Funcionario> getFuncionarios() {
return funcionarios;
}
public void setFuncionarios(List<Funcionario> funcionarios) {
this.funcionarios = funcionarios;
}
public void setArquivo(StreamedContent arquivo) {
this.arquivo = arquivo;
}
public StreamedContent getArquivo() throws IOException {
return arquivo;
}
public void botaoArquivoFuncionarios() throws IOException {
//Abre classes de escrita em arquivo
this.nomeArq = "ListaFuncionarios" + this.arquivoDiariasDFI.getDataBase().replace("/","") + ".csv";
File f = this.gerarArquivoFuncionarios();
FileInputStream stream = new FileInputStream (f);
arquivo = new DefaultStreamedContent(stream, "", nomeArq);
}
public File gerarArquivoFuncionarios() throws IOException {
File f = new File(this.nomeArq);
FileOutputStream fos = null;
PrintStream ps = null;
try {
fos = new FileOutputStream(f);
ps = new PrintStream(fos);
//Carrega todos os funcionários
this.setFuncionarios(funcionarioManagerLocal.getFuncionarioAnual(1)); //Pega funcionarios com data_fim ate um ano atras
ps.println("CPF, NOME, CARGO");
for (Funcionario func : funcionarios) {
ps.println(String.format("%s, %s, %s", func.getNoCpf(), func.getNmFuncionario(), func.getCargo().getDsCargo() ));
}
ps.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (fos != null) {
fos.close();
}
if (ps != null ) {
ps.close();
}
return f;
} catch (Exception e) {
e.printStackTrace();
}
}
return f;
}
//
public String abrirTelaDeEnvio() {
this.setDatasBases(this.carregaDatasBases());
//Foi selecionado um registro (data base preenchida)
if (arquivoDiariasDFI != null) {
this.setStatus("tela2_alteracao");
this.desabDataBase = true;
//carrega arquivo de acordo com processamento.
} else {
this.setStatus("tela2_novo");
this.desabDataBase = false;
arquivoDiariasDFI = new ArquivoDiariasDFI();
arquivoDiariasDFI.setSituacao(1);
this.setDatasBases(this.carregaDatasBases());
}
return "arquivoDiariasDFI_detalhe.xhtml"; //retorno para navegação.
}
//Carrega lista de Meses Bases: de hoje até 1 ano atrás.
//Já retorna convertida para String.
public List < String > carregaDatasBases() {
ArrayList < String > retorno = new ArrayList<String>();
Calendar c = Calendar.getInstance();
c.setTime( new Date());
Date data = c.getTime();
retorno.add(formataMesBase(data));
int incremento = 1;
while (incremento <= 12) {
c.add(Calendar.MONTH, -1 );
data = c.getTime();
retorno.add( formataMesBase(data) );
incremento ++;
}
return retorno;
}
public String formataMesBase(Date pData) {
SimpleDateFormat sDF = new SimpleDateFormat("MM/yyyy");
String data = sDF.format(pData);
return data;
}
// CONVERTERS
// CONVERSOR PERSONALIZADO PARA SELECTIONBOX DE DATA BASE
public Converter getdataBaseConverter(){
return dataBaseConverter;
}
private Converter dataBaseConverter = new Converter() {
@Override
public Object getAsObject(FacesContext fc, UIComponent comp, String pValue) {
if (pValue == null || pValue.equals("") || pValue.isEmpty()) {
return null;
} else {
return Integer.parseInt(pValue);
}
}
@Override
public String getAsString(FacesContext context, UIComponent comp, Object pDataBase) {
if(pDataBase == null || pDataBase.equals("")) {
return null;
} else {
return String.valueOf(pDataBase);
}
}
}; //fim do converter
}
我希望单击按钮时文件下载能够正常工作。
解决方案
该文件必须由其在 de ManagedBean 中的 get 方法生成和返回。如果文件生成在 get 方法之外,则 FileDownload 不起作用。(至少在这个 Primefaces 版本中)。
推荐阅读
- java - 如何使物体随条件移动
- reactjs - 如何限制 react-currency-input 中的位数
- actions-on-google - Image donot show in rich response in actions on Google
- javascript - Html2canvas 不工作 highcharts to png 在 Firefox 和 IE 上不工作
- function - Azure function wants two versions of the same DLL
- chef-infra - 如何根据厨师中的节点角色执行任务
- angular - Angular如何以“加密”方式将数据发送到支持?
- python - 对熊猫中的前 2 列最大值进行分组
- java - evaluateFormulaCellEnum(cell) 无法使用 POI 理解 Excel 中的公式
- angularjs - AngularJS - directive for only numbers with decimal places