Como usar GraphicImage no Primefaces

PrimeFaces
PrimeFaces

Neste exemplo temos uma alternativa para usar o GraphicImage no PrimeFaces com JSF 2 para mostrar uma imagem gravada no banco de dados.

O GraphicImage é um componente prático porque elimina a necessidade de servlets ou outros artifícios para carregar a imagem.

Um ponto importante é que o GraphicImage não funciona com @ViewScope, que é muito usado nas aplicações com JSF 2. Ele funciona apenas com @SessionScope e @RequestScope. Para resolver esse problema, criei o ImagemProdutoBean, um managed bean com @RequestScope.

Código-fonte

O projeto completo está disponível no GitHub. Além do GraphicImage, o projeto mostra exemplos de outros componentes PrimeFaces, além de soluções simples para problemas comuns de aplicações web.

Persistência

No nosso exemplo vamos criar a classe Produto com o atributo byte[] foto, que deve ter a anotação @Lob. Em alguns bancos de dados temos que definir um tamanho desse arquivo, como é o caso do HSQLDB, que é usado no exemplo. No MySQL não é necessário.


public class Produto {
    {...}
    @Lob
    @Column(length = 1024 * 1024 * 5)
    private byte[] especificacaoFabricante;
    @Lob
    @Column(length = 1024 * 1024 * 5)
    private byte[] foto;
    {...}
}

JSF

A página produto.xhtml vai mostrar a imagem através do managed bean imagemProdutoBean.


    <h:form enctype="multipart/form-data">

{...}

          <p:outputLabel value="Arquivo da foto" />
          <p:fileUpload value="#{produtoBean.foto}" mode="simple"
            id="uploadfotoProduto" />

          <p:outputLabel value="Arquivo da foto" />
          <p:fileUpload value="#{produtoBean.foto}" mode="simple"
            id="uploadfotoProduto" />

          <p:outputLabel value="Foto" rendered="#{produtoBean.existeFoto}" />
          <p:graphicImage cache="false" rendered="#{produtoBean.existeFoto}" value="#{imagemProdutoBean.conteudoImagem}">
            <f:param value="#{produtoBean.produto.id}" name="id" />
          </p:graphicImage>

          <p:commandButton value="Salvar" ajax="false" actionListener="#{produtoBean.salvar}" icon="ui-icon-disk" update="messages" />
          <p:commandButton value="Salvar" ajax="false"
            actionListener="#{produtoBean.salvar}" icon="ui-icon-disk"
            update="messages" />

{...}

    </h:form>

Managed bean

Precisamos de 2 managed beans. O primeiro, ProdutoBean, é para a página XHTML e tem escopo @ViewScoped. O segundo, ImagemProdutoBean é somente para a imagem e pode ser @RequestScoped ou @SessionScoped. Eu prefiro o @RequestScoped, mas fica a critério de cada um.

Observe o método salvar(), que deve ter uma condição para não apagar a imagem já gravada, no caso em que o usuário está fazendo a edição do registro.

Para esse exemplo, o ajax do upload está desabilitado. Para habilitar o ajax, você pode usar essa dica.


@ViewScoped
@ManagedBean
public class ProdutoBean {

    {...}

    private UploadedFile foto;
    private Produto produto;

    public void setFoto(UploadedFile foto) {
        this.foto = foto;
    }

    public UploadedFile getFoto() {
        return foto;
    }

    public boolean isExisteFoto() {
        try {
            return getProduto().getFoto().length > 0;
        } catch (Exception e) {
            return false;
        }
    }

    public void salvar() {
        try {
            //
            if (getEspecificacaoFabricante() != null
                    && getEspecificacaoFabricante().getSize() > 0) {
                byte[] dados = IOUtils.toByteArray(getEspecificacaoFabricante()
                        .getInputstream());
                getProduto().setEspecificacaoFabricante(dados);
            }
            //
            if (getFoto() != null && getFoto().getSize() > 0) {
                byte[] dados = IOUtils.toByteArray(getFoto().getInputstream());
                getProduto().setFoto(dados);
            }
            //
            getProdutoService().salvar(getProduto());
            infoMsg(MENSAGEM_SUCESSO_GRAVACAO);
        } catch (Exception e) {
            errorMsg(e);
        }
    }

}

GraphicImage no Primefaces

Essa é a abordagem mais simples para mostrar uma imagem no PrimeFaces. E pode ser usada no sistema todo.

Um detalhe é que o PrimeFaces executa duas vezes o método getConteudoImagem() e somente da segunda vez é que a imagem deve ser carregada.



package net.marcoreis.ecommerce.controlador;

import java.io.ByteArrayInputStream;
import java.io.InputStream;

import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseId;

import net.marcoreis.ecommerce.entidades.Produto;
import net.marcoreis.ecommerce.negocio.GenericService;

import org.primefaces.model.DefaultStreamedContent;
import org.primefaces.model.StreamedContent;

@ManagedBean
@RequestScoped
public class ImagemProdutoBean extends BaseBean {
    private static final long serialVersionUID = -7524476303834771432L;
    private Produto produto;

    @PostConstruct
    public void init() {
        produto = new Produto();
    }

    public StreamedContent getConteudoImagem() {
        try {
            FacesContext context = FacesContext.getCurrentInstance();
            if (context.getCurrentPhaseId() == PhaseId.RENDER_RESPONSE) {
                return new DefaultStreamedContent();
            }
            String idS = getParametro("id");
            Long id = Long.parseLong(idS);
            produto = (Produto) new GenericService()
                    .findById(Produto.class, id);
            InputStream is = new ByteArrayInputStream(produto.getFoto());
            DefaultStreamedContent dsc = new DefaultStreamedContent(is);
            return dsc;
        } catch (Exception e) {
            return new DefaultStreamedContent();
        }
    }

}