JSF Download File Saved in Database

Asked

Viewed 619 times

1

Hello, I have a jsf page where I upload the data from a entidade, between the data there is an attached file (pdf, xls, jpg etc.). I would like you, when clicking on the file name, to download the file. If anyone can help in any way thank you.

Method I use to add the file :

public void doUpload(FileUploadEvent fileUploadEvent) throws IOException {

        UploadedFile uploadedFile = fileUploadEvent.getFile();
        String fileNameUploaded = uploadedFile.getFileName();
        long fileSizeUploaded = uploadedFile.getSize();
        // System.out.println(uploadedFile);

        String infoAboutFile = "<br/> Arquivo recebido: <b>" + fileNameUploaded + "</b><br/>"
                + "Tamanho do Arquivo: <b>" + fileSizeUploaded + "</b>";

        FacesContext facesContext = FacesContext.getCurrentInstance();
        facesContext.addMessage(null, new FacesMessage("Sucesso", infoAboutFile));

        arquivo = (IOUtils.toByteArray(uploadedFile.getInputstream()));
        entradaAcidente.setAttach(arquivo);
        nomeArquivo = uploadedFile.getFileName();

    }

Entity:

@Entity
@Table(name = "entrada_acidente")
public class EntradaAcidente implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long id;
    private Date dataCriacao;
    private String observacao;
    private BigDecimal valorDesconto = BigDecimal.ZERO;
    private BigDecimal valorTotal = BigDecimal.ZERO;
    private StatusEntradaAcidente status = StatusEntradaAcidente.ORCAMENTO;
    private FormaPagamento formaPagamento;
    private Usuario vendedor;
    private Cliente cliente;
    private Ocorrencia ocorrencia1;
    private List<ItemDespesa> itens = new ArrayList<>();

    @Lob
    @Column(columnDefinition = "LONGBLOB")
    private byte[] comprovante;
    private String fileName;

    public String getFileName() {
        return fileName;
    }

    public void setFileName(String fileName) {
        this.fileName = fileName;
    }

    @Id
    @GeneratedValue
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    @Lob
    public byte[] getComprovante() {
        return comprovante;
    }
    public void setComprovante(byte[] comprovante) {
        this.comprovante = comprovante;
    }

    @NotNull
    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "data_criacao", nullable = false)
    public Date getDataCriacao() {
        return dataCriacao;
    }

    public void setDataCriacao(Date dataCriacao) {
        this.dataCriacao = dataCriacao;
    }

    @Column(columnDefinition = "text")
    public String getObservacao() {
        return observacao;
    }

    public void setObservacao(String observacao) {
        this.observacao = observacao;
    }



    @NotNull
    @Column(name = "valor_desconto", nullable = false, precision = 10, scale = 2)
    public BigDecimal getValorDesconto() {
        return valorDesconto;
    }

    public void setValorDesconto(BigDecimal valorDesconto) {
        this.valorDesconto = valorDesconto;
    }

    @NotNull
    @Column(name = "valor_total", nullable = false, precision = 10, scale = 2)
    public BigDecimal getValorTotal() {
        return valorTotal;
    }

    public void setValorTotal(BigDecimal valorTotal) {
        this.valorTotal = valorTotal;
    }

    @NotNull
    @Enumerated(EnumType.STRING)
    @Column(nullable = false, length = 20)
    public StatusEntradaAcidente getStatus() {
        return status;
    }

    public void setStatus(StatusEntradaAcidente status) {
        this.status = status;
    }

    @NotNull
    @Enumerated(EnumType.STRING)
    @Column(name = "forma_pagamento", nullable = false, length = 20)
    public FormaPagamento getFormaPagamento() {
        return formaPagamento;
    }

    public void setFormaPagamento(FormaPagamento formaPagamento) {
        this.formaPagamento = formaPagamento;
    }

    @NotNull
    @ManyToOne
    @JoinColumn(name = "vendedor_id", nullable = false)
    public Usuario getVendedor() {
        return vendedor;
    }

    public void setVendedor(Usuario vendedor) {
        this.vendedor = vendedor;
    }

    @NotNull
    @ManyToOne
    @JoinColumn(name = "cliente_id", nullable = false)
    public Cliente getCliente() {
        return cliente;
    }

    public void setCliente(Cliente cliente) {
        this.cliente = cliente;
    }

    @ManyToOne
    @JoinColumn(name = "ocorrencia_id", nullable = false)
    public Ocorrencia getOcorrencia() {
        return ocorrencia1;
    }

    public void setOcorrencia(Ocorrencia ocorrencia) {
        this.ocorrencia1 = ocorrencia;
    }

    @OneToMany(mappedBy = "entradaAcidente", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
    public List<ItemDespesa> getItens() {
        return itens;
    }

    public void setItens(List<ItemDespesa> itens) {
        this.itens = itens;
    }

    @Transient
    public boolean isNovo() {
        return getId() == null;
    }

    @Transient
    public boolean isExistente() {
        return !isNovo();
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((id == null) ? 0 : id.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        EntradaAcidente other = (EntradaAcidente) obj;
        if (id == null) {
            if (other.id != null)
                return false;
        } else if (!id.equals(other.id))
            return false;
        return true;
    }

    @Transient
    public BigDecimal getValorSubtotal() {
        return this.getValorTotal().subtract(this.getValorDesconto());
    }

    public void recalcularValorTotal() {
        BigDecimal total = BigDecimal.ZERO;

        total = total.subtract(this.getValorDesconto());

        for (ItemDespesa item : this.getItens()) {
            if (item.getDespesa() != null && item.getDespesa().getId() != null) {
                total = total.add(item.getValorTotal());
            }
        }

        this.setValorTotal(total);
    }

    public void adicionarItemVazio() {
        if (this.isOrcamento()) {
            Despesa despesa = new Despesa();

            ItemDespesa item = new ItemDespesa();
            item.setDespesa(despesa);
            item.setEntradaAcidente(this);

            this.getItens().add(0, item);
        }
    }

    @Transient
    public boolean isOrcamento() {
        return StatusEntradaAcidente.ORCAMENTO.equals(this.getStatus());
    }

    public void removerItemVazio() {
        ItemDespesa primeiroItem = this.getItens().get(0);

        if (primeiroItem != null && primeiroItem.getDespesa().getId() == null) {
            this.getItens().remove(0);
        }
    }

    @Transient
    public boolean isValorTotalNegativo() {
        return this.getValorTotal().compareTo(BigDecimal.ZERO) < 0;
    }

    @Transient
    public boolean isEmitido() {
        return StatusEntradaAcidente.EMITIDO.equals(this.getStatus());
    }

    @Transient
    public boolean isNaoEmissivel() {
        return !this.isEmissivel();
    }

    @Transient
    public boolean isEmissivel() {
        return this.isExistente() && this.isOrcamento();
    }

    @Transient
    public boolean isCancelavel() {
        return this.isExistente() && !this.isCancelado();
    }

    @Transient
    private boolean isCancelado() {
        return StatusEntradaAcidente.CANCELADO.equals(this.getStatus());
    }

    @Transient
    public boolean isNaoCancelavel() {
        return !this.isCancelavel();
    }

    @Transient
    public boolean isAlteravel() {
        return this.isOrcamento();
    }

    @Transient
    public boolean isNaoAlteravel() {
        return !this.isAlteravel();
    }

    @Transient
    public boolean isNaoEnviavelPorEmail() {
        return this.isNovo() || this.isCancelado();
    }

}

Class where the download method is:

@Named
@ViewScoped
public class PesquisaEntradaAcidentesBean implements Serializable {

    private static final long serialVersionUID = 1L;

    @Inject
    private EntradaAcidentes entradas;

    private AcidenteFilter filtro;
    private List<EntradaAcidente> entradasFiltradas;
    private EntradaAcidente entradaSelecionada;

    public PesquisaEntradaAcidentesBean() {
        filtro = new AcidenteFilter();
        entradasFiltradas = new ArrayList<>();
    }

    public void pesquisar() {
        entradasFiltradas = entradas.filtrados(filtro);
    }

    public StatusEntradaAcidente[] getStatuses() {
        return StatusEntradaAcidente.values();
    }

    public List<EntradaAcidente> getEntradasFiltradas() {
        return entradasFiltradas;
    }

    public AcidenteFilter getFiltro() {
        return filtro;
    }

    public EntradaAcidente getEntradaSelecionada() {
        return entradaSelecionada;
    }

    public void setEntradaSelecionada(EntradaAcidente entradaSelecionada) {
        this.entradaSelecionada = entradaSelecionada;
    }

    public String downloadComprovante() throws IOException {
        String nomeArquivo = "comprovante_" + entradaSelecionada.getId() + ".pdf";
        FacesContext facesContext = FacesContext.getCurrentInstance();
        ExternalContext externalContext = facesContext.getExternalContext();

        externalContext.responseReset();
        externalContext.setResponseContentType("application/pdf");
        externalContext.setResponseHeader("Content-Disposition", "attachment; filename=\"" + nomeArquivo + "\"");

        OutputStream out = externalContext.getResponseOutputStream();

        try (InputStream is = new ByteArrayInputStream(entradaSelecionada.getComprovante())) {
            int read = -1;
            byte[] buffer = new byte[1024];

            while ((read = is.read(buffer)) != -1) {
                out.write(buffer, 0, read);
            }
        }

        facesContext.responseComplete();
        return nomeArquivo;

    }

}

jsf:

<ui:composition template="/WEB-INF/template/LayoutPadrao.xhtml"
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:p="http://primefaces.org/ui">

    <ui:define name="titulo">Pesquisa de acidentes</ui:define>

    <ui:define name="corpo">
        <h:form>
            <h1>Pesquisa de acidentes</h1>

            <p:toolbar style="margin-top: 20px">
                <p:toolbarGroup>
                    <p:commandButton value="Pesquisar"
                        action="#{pesquisaEntradaAcidentesBean.pesquisar}" update="@form" />
                </p:toolbarGroup>
                <p:toolbarGroup align="right">
                    <p:button value="Novo" outcome="/entradasAcidente/CadastroEntradaAcidente" />
                </p:toolbarGroup>
            </p:toolbar>

            <p:panelGrid columns="2" id="painel"
                style="width: 100%; margin-top: 20px" columnClasses="rotulo, campo">
                <p:outputLabel value="Número" />
                <h:panelGroup>
                    <p:inputText size="10"
                        value="#{pesquisaEntradaAcidentesBean.filtro.numeroDe}" />

                    <p:inputText size="10"
                        value="#{pesquisaEntradaAcidentesBean.filtro.numeroAte}" />
                </h:panelGroup>



                <p:outputLabel value="Data de criação" />
                <h:panelGroup>
                    <p:calendar size="10" pattern="dd/MM/yyyy"
                        value="#{pesquisaEntradaAcidentesBean.filtro.dataCriacaoDe}" />

                    <p:calendar size="10" pattern="dd/MM/yyyy"
                        value="#{pesquisaEntradaAcidentesBean.filtro.dataCriacaoAte}" />
                </h:panelGroup>

                <p:outputLabel value="Responsável" />
                <p:inputText size="40"
                    value="#{pesquisaEntradaAcidentesBean.filtro.nomeVendedor}" />

                <p:outputLabel value="Cliente" />
                <p:inputText size="40"
                    value="#{pesquisaEntradaAcidentesBean.filtro.nomeCliente}" />

                <p:outputLabel value="Status" />
                <p:selectManyCheckbox
                    value="#{pesquisaEntradaAcidentesBean.filtro.statuses}">
                    <f:selectItems value="#{pesquisaEntradaAcidentesBean.statuses}"
                        var="status" itemValue="#{status}" itemLabel="#{status.descricao}" />
                </p:selectManyCheckbox>
            </p:panelGrid>

            <p:dataTable id="acidentesTable"
                value="#{pesquisaEntradaAcidentesBean.entradasAcidenteFiltrados}"
                var="entrada_acidente" style="margin-top: 20px"
                emptyMessage="Nenhum  encontrado." rows="20" paginator="true"
                paginatorAlwaysVisible="false" paginatorPosition="bottom">
                <p:column headerText="Número"
                    style="text-align: center; width: 100px">
                    <h:outputText value="#{entrada_acidente.id}" />
                </p:column>
                <p:column headerText="Cliente">
                    <h:outputText value="#{entrada_acidente.cliente.nome}" />
                </p:column>
                <p:column headerText="Ocorrencia">
                    <h:outputText value="#{entrada_acidente.ocorrencia.descricao}" />
                </p:column>
                <p:column headerText="Responsável">
                    <h:outputText value="#{entrada_acidente.vendedor.nome}" />
                </p:column>
                <p:column headerText="Data de criação"
                    style="text-align: center; width: 140px">
                    <h:outputText value="#{entrada_acidente.dataCriacao}">
                        <f:convertDateTime pattern="dd/MM/yyyy" />
                    </h:outputText>
                </p:column>
                <p:column headerText="Valor total"
                    style="text-align: right; width: 120px">
                    <h:outputText value="#{entrada_acidente.valorTotal}">
                        <f:convertNumber type="currency" />
                    </h:outputText>
                </p:column>
                <p:column headerText="Status" style="width: 100px">
                    <h:outputText value="#{entrada_acidente.status.descricao}" />
                </p:column>
                <p:column headerText="Arquivo">
                    <h:outputText value="#{entrada_acidente.fileName}" />
                </p:column>



                <p:column headerText="Download">
                    <h:commandButton
                        action="#{pesqusaEntradaAcidentesBean.downloadComprovante}">

                        <f:setPropertyActionListener
                            target="#{pesqusaEntradaAcidentesBean.entradaSelecionada}"
                            value="entrada_acidente.id" />
                        <f:param name="entrada_acidente" value="#{entrada_acidente.id}" />
                    </h:commandButton>
                </p:column>

                <p:column style="text-align: center; width: 50px">
                    <p:button icon="ui-icon-pencil" title="Editar"
                        outcome="/entradasAcidente/CadastroEntradaAcidente">
                        <f:param name="entrada_acidente" value="#{entrada_acidente.id}" />
                    </p:button>
                </p:column>
            </p:dataTable>

        </h:form>
    </ui:define>
</ui:composition>

Data display: inserir a descrição da imagem aqui

Update:

Downloadbean:

@ManagedBean
@RequestScoped
public class DownloadBean extends HttpServlet{
    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    private StreamedContent file;
    private int codigo;

    public StreamedContent getFile() {
        return file;
    }

    public void setFile(StreamedContent file) {
        this.file = file;
    }

    public int getCodigo() {
        return codigo;
    }

    public void setCodigo(int codigo) {
        this.codigo = codigo;
    }

    public void download() {
        ResultSet rs;

        try {
            byte[] bytes = null;

            Class.forName("com.mysql.jdbc.Driver");
            java.sql.Connection con = DriverManager.getConnection("jdbc:mysql://localhost/PRODUTOS", "root", "123456");
            PreparedStatement ps = con.prepareStatement("select attach from ENTRADA_ACIDENTE where id= (?)");

            ps.setInt(1, codigo);
            rs = ps.executeQuery();
            while (rs.next()) {
                bytes = rs.getBytes("attach");
            }

            ps.close();
            con.close();

            FacesContext facesContext = FacesContext.getCurrentInstance();
            ExternalContext externalContext = facesContext.getExternalContext();
            HttpServletResponse response = (HttpServletResponse) externalContext.getResponse();
            response.reset();
            response.getOutputStream().write(bytes);
            response.getOutputStream().flush();
            response.getOutputStream().close();

            FacesContext.getCurrentInstance().responseComplete();

        } catch (Exception e) {
            FacesMessage message = new FacesMessage("Erro");
            FacesContext.getCurrentInstance().addMessage(null, message);
        }
    }
    }

xhtml download:

        <p:column headerText="Download">
            <h:commandLink id="getDownload" value="download"
                action="#{downloadBean.download()}">
                <f:setPropertyActionListener
                    target="#{downloadBean.codigo}"
                    value="#{entrada_acidente.id}" />
            </h:commandLink>
        </p:column>

xhtml upload:

web xml.

<servlet>
        <servlet-name>DownloadBean</servlet-name>
        <servlet-class>com.rodrigo.acidentes.controller.DownloadBean</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>DownloadBean</servlet-name>
        <url-pattern>/image/*</url-pattern>
    </servlet-mapping>

Update 2:

DownloadBean:

    package com.rodrigo.controleacidentes.controller;

    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;

    import javax.faces.application.FacesMessage;
    import javax.faces.bean.ManagedBean;
    import javax.faces.bean.SessionScoped;
    import javax.faces.context.FacesContext;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletResponse;

    import org.primefaces.model.StreamedContent;

    @ManagedBean
    @SessionScoped
    public class DownloadBean extends HttpServlet {

        private static final long serialVersionUID = 1L;
        private StreamedContent file;
        private long codigo;

        public StreamedContent getFile() {
            return file;
        }

        public void setFile(StreamedContent file) {
            this.file = file;
        }

        public long getCodigo() {
            return codigo;
        }

        public void setCodigo(long codigo) {
            this.codigo = codigo;
        }

        public void download(Long codigo) {
            ResultSet rs;
            byte[] bytes = null;
            String fileName = "";
            try {

                Class.forName("com.mysql.jdbc.Driver");
                Connection con = DriverManager.getConnection("jdbc:mysql://localhost/acidentes", "root", "123456");
                PreparedStatement ps = con
                        .prepareStatement("select attach,fileName from ENTRADA_ACIDENTE where ENTRADA_ACIDENTE.id=?");

                ps.setLong(1, codigo);
                rs = ps.executeQuery();
                while (rs.next()) {
                    bytes = rs.getBytes("attach");
                    fileName = rs.getString("fileName");
                }
                ps.close();
                con.close();

                HttpServletResponse response = (HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext()
                        .getResponse();

                response.setContentType("application/octet-stream");
                response.setHeader("Content-disposition", "attachment; filename=" + fileName);
                response.getOutputStream().write(bytes);
                response.getOutputStream().flush();
                response.getOutputStream().close();
                FacesContext.getCurrentInstance().responseComplete();

            } catch (Exception e) {
                FacesMessage message = new FacesMessage("Erro ao realizar download!");
                FacesContext.getCurrentInstance().addMessage(null, message);
                e.printStackTrace();
            }
        }
    }

xhtml:

    <p:column headerText="Download">    
                            <p:commandButton value="Download"
                            actionListener="#{downloadBean.download(entrada_.id)}"
                            ajax="false">
                            <p:fileDownload value="#{downloadBean.file}" />
                        </p:commandButton>
                    </p:column>
  • 1

    Rodriho, I think you will need to persist more information about the file, such as the name / extension. With this information you can develop a Servlet or something like that configure the return type and write the content of blob in stream response. From a look in that reply at Balusc no Soen. You can adapt the query in Servlet to JPA and link the file column of your table to [caminho do servlet/id] according to your design.

  • I added the file name in the meantime.

  • @Anthony Accioly I changed the codes in the question, including a method to download, but obviously it is factoring something, or there is something wrong, by clicking the download button just redirects to the search page. If there’s anything I can do, thank you.

  • 1

    Try to pass the id or the object itself via el to the download action (I’m suspicious of this combination with setProperty + param). Also make your download method return void and put in some logs to make sure the method is being called (another example of Balusc). Give me a call if it works.

  • Also lacked the Content-Length

  • I’m trying, but even if I call any other method, the behavior is the same, as if it’s not even triggering the so-called method. Also if you put a " system.out" to print on the console, it does not appear.

  • @Anthony Accioly I was able to upload it, I saw it in my own browser. I added the codes above. However, when downloading from the browser, the image is corrupted, although in the visualization it is "perfect". I am still working on the solutions, but without success. In some moments this error happens: java.lang.IllegalStateException: getOutputStream() has already been called for this response.

  • 1

    See if your download method is not being called multiple times (it is because of problems like this that I always move logic to a Servlet).

  • Missing also include Content type and length headers in response.

  • I’ll check, thank you.

  • I could see that even when running the search method, before the data appear and then click on the download link runs the download method.

  • @Anthony Accioly I solved the download problem as codes posted in "update 2" I believe it can be improved, (even if someone has suggestions) but it is functional at the moment. In some moments there is still the error of : getOutputStream() has already been called for this response, at first if I perform a registration operation and then download . This I have not solved yet.

Show 7 more comments
No answers

Browser other questions tagged

You are not signed in. Login or sign up in order to post.