首页 > 解决方案 > 如何修复 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



}


我希望单击按钮时文件下载能够正常工作。

标签: jsfprimefacesdownload

解决方案


该文件必须由其在 de ManagedBean 中的 get 方法生成和返回。如果文件生成在 get 方法之外,则 FileDownload 不起作用。(至少在这个 Primefaces 版本中)。


推荐阅读