Sempre Java

dezembro 4, 2011

Leitura e gravação de imagem em JSF com banco de dados

Olá pessoal, bom dia.
O que me motivou a estar escrevendo esse post foi a dificuldade em encontrar um artigo que descrevesse como gravar imagem no banco de dados
utilizando JSF. Tentei utilizar o componente do primefaces p:graphicImage, porém o mesmo só funciona atrelado a um managed bean com escopo
de sessão, caso contrário, durante o upload a imagem enviada é perdida para uma exibição posterior.
Com isso durante algumas pesquisas, resolvi utilizar o componente do tomahawk, o t:inputFileUpload.
Irei demonstrar a utilização, dividindo o código em três partes, a página JSF, o Validator e o ManagedBean.
Não irei postar aqui a camada de persistência nem a de serviço, pois utilizo injeção de dependências o que caberá a um outro post.
Então vamos lá:

Página JSF (meuPerfil.xhtml):


<?xml version='1.0' encoding='UTF-8' ?>
<!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:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.prime.com.tr/ui"
xmlns:t="http://myfaces.apache.org/tomahawk"
xmlns:f="http://java.sun.com/jsf/core">

<h:head>

<ui:insert name="title">
<title>Upload de imagem</title>
</ui:insert>

<link href="${facesContext.externalContext.requestContextPath}/resources/css/layout.css" rel="stylesheet"
type="text/css" />
<link href="${facesContext.externalContext.requestContextPath}/resources/css/reset.css" rel="stylesheet"
type="text/css" />

<!--[if IE]>
<link href="${facesContext.externalContext.requestContextPath}/resources/css/ie.css" rel="stylesheet" type="text/css" />
<![endif]-->
<ui:insert name="javascript" />

<ui:insert name="css" />

</h:head>

<h:body onload="loadPage()">
<f:view>
<h:form id="form" prependId="false" enctype="multipart/form-data">

<div id="wrap">

<div id="topo">

<div id="logo">
<h:graphicImage url="/resources/img/logo.png" />
</div>

<div id="nav">
<ul id="menu">
<li>
<h:commandLink value="Home" action="/index?faces-redirect=true" immediate="true">
<f:ajax execute="@this" render="@none"/>
</h:commandLink>
</li>
<li><h:commandLink value="#{msg['menu.minhaConta']}" action="/jsf/users/user/minhaConta.xhtml?faces-redirect=true" rendered="#{usuarioController.exibeUsuarioAutenticado}" /></li>
<li><h:commandLink value="Vestidos" action="vestido.xhtml" immediate="true" /></li>
<li><h:commandLink value="Alianças" action="alianca.xhtml" immediate="true" /></li>
<li><h:commandLink value="Flores" action="flor.xhtml" immediate="true" /></li>
<li><h:commandLink value="Convites" action="convite.xhtml" immediate="true" /></li>
<li><h:commandLink value="Sapatos" action="sapato.xhtml" immediate="true" /></li>
<li><h:commandLink value="Sair" action="sair.xhtml" immediate="true" /></li>
</ul>
</div>

</div>

<div id="content">

<div id="anunciantes">

</div>

<div id="corpo">

<div id="esquerda">

<p:message showDetail="true" showSummary="false" for="fileUpload" />

<div style="margin-top: 10px; text-align: center">

<h:graphicImage id="imgUsurio" value="#{usuarioController.image}" width="140px;" />
<t:inputFileUpload id="fileUpload" value="#{usuarioController.uploadedFile}" accept="*.jpeg, *.png, *.gif, *.jpg" style="display: none;"
onchange="jQuery('#cdlSubmit').click();" validator="#{usuarioValidator.validate}" />
<h:commandButton id="cdlSubmit" action="#{usuarioController.submit}" style="display: none;" />
<h:commandLink type="button" value="Alterar foto" onclick="jQuery('#fileUpload').click();" style="font-size: 10px;" rendered="#{!empty usuarioController.image}">
<f:ajax event="click" execute="@this" />
</h:commandLink>
<h:commandLink type="button" value="Adicionar foto" onclick="jQuery('#fileUpload').click();" style="font-size: 10px;" rendered="#{empty usuarioController.image}">
<f:ajax event="click" execute="@this" />
</h:commandLink>

</div>

</div>

<div id="centro">

<!-- Seu conteúdo do centro -->

</div>

<div id="direita">

<!-- Seu conteúdo da direita -->

</div>

</div>

<div id="footer">
<h:outputLabel style="color: #000000;" value="#{msg['mensagem.rodape']}" />
</div>

</div>

</div>

</h:form>
</f:view>
</h:body>
</html>

O trecho de código acima possui uma implementação onde escondo o botão “upload” padrão do componente t:inputFileUpload, e adiciono
um commmandLink que faz a chamada do botão upload através de javascript.

Managed Bean (UsuarioController.java):


package br.com.pyramides.controller;

import br.com.pyramides.jsf.FacesUtils;
import br.com.pyramides.model.Usuario;
import br.com.pyramides.model.UsuarioImagem;
import br.com.pyramides.util.Constantes;
import br.com.pyramides.util.CriptografaSenha;
import java.io.File;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.faces.component.UIComponent;
import javax.faces.component.html.HtmlInputHidden;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.FacesConverter;
import javax.faces.model.SelectItem;
import javax.imageio.stream.FileImageOutputStream;
import javax.inject.Named;
import javax.servlet.ServletContext;
import org.apache.myfaces.custom.fileupload.UploadedFile;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;

/**
*
* @author Lessandro
*/

//Caso você não esteja utilizando injeção de dependências, substitua o @Controller pelo @ManagedBean,
//o @Scope("view") pelo @ViewScoped e remova o @Named, pois o nome seria definido da seguinte maneira
//@ManagedBean(name="usuarioController")
@Named("usuarioController")
@Controller
@Scope("view")
public class UsuarioController implements Serializable {

private static final long serialVersionUID = 1L;
private Usuario usuario;
private String mensagem;
private boolean somenteLeitura, exibeUsuarioAutenticado;
private String image;
private UploadedFile uploadedFile;
private static final String EXTENSION = "png";

@PostConstruct
public void init() {
//
// Chamado só quando o managed bean é colocado no escopo view, e não a
// cada requisição como acontecia com o escopo request
//
String currentPage = FacesContext.getCurrentInstance().getViewRoot().getViewId();
if (currentPage.indexOf("meuPerfil.xhtml") != -1) {
preparaUsuario();
geraImagemServidor();
}
}

@PreDestroy
public void destroy() {
//
// chamado quando outra view for chamada através do
// UIViewRoot.setViewId(String viewId)
//
setSomenteLeitura(false);
}

//
// Método que realiza o upload da imagem para a base.  Reparem que tenho o objeto Usuario que está vinculado a um objeto
// UsuarioImagem. O nome da imagem para o objeto UsuarioImagem é o nome do usuário criptografado com MD5 mais a extensão,
// para que eu consiga realizar uma certa segurança, ficando algo como "dsahe37436dahg43rfd4da743743djs.png".
// A foto é um array de bytes, e a extensão é a mesma do arquivo que foi realizado Upload.
// Reparem também que existe uma propriedade setThumbnail, pois a imagem que gero no servidor e na base é um thumbnail, pois
// no meu caso não tenho a necessidade de exibir imagem maiores. Caso não queiram reduzir o tamanho da imagem, basta apenas
// setar a propriedade setFoto() com o array de bytes real.
//
public void submit() {
try {
byte[] bytes = uploadedFile.getBytes();
byte[] thumbBytes = setThumbnail(bytes, EXTENSION);
UsuarioImagem usuarioImagem = usuario.getUsuarioImagem() != null ? usuario.getUsuarioImagem() : new UsuarioImagem();
usuarioImagem.setUsuario(usuario);
String nomeImagem = CriptografaSenha.criptografar(usuario.getNome()) + "." + EXTENSION;
usuarioImagem.setNome(nomeImagem);
usuarioImagem.setFoto(thumbBytes);
usuarioImagem.setExtensao(EXTENSION);
usuario.setUsuarioImagem(usuarioImagem);
usuario.setPassword(getUsuarioAutenticado().getPassword());
// Chama o método "atualizar" do DAO responsável por realizar um "update" na tabela, não disponibilizei aqui por se tratar
//de um DAO já abordado em outros posts.
//
UsuarioDAO usuarioDAO = new UsuarioDAO();
usuarioDAO.atualiza(usuario);
geraImagemServidor();
} catch (Exception ex) {
System.out.println("Erro " + ex.getMessage());
}
}

//
// Método que carrega a imagem vinculada ao objeto usuário, e cria um arquivo com os bytes desta imagem no servidor,
// dentro da pasta resources/img/users, da mesma forma como fiz o upload, o nome da imagem é o nome do usuário criptografado
// com a extensão png.
//
private void geraImagemServidor() {
try {
if (usuario.getUsuarioImagem() != null) {
FacesContext context = FacesContext.getCurrentInstance();
ServletContext servletContext = (ServletContext) context.getExternalContext().getContext();
String imageUsers = servletContext.getRealPath("/resources/img/users");
File dirImageUsers = new File(imageUsers);
if (!dirImageUsers.exists()) {
dirImageUsers.createNewFile();
}

byte[] bytes = usuario.getUsuarioImagem().getFoto();
FileImageOutputStream imageOutput = new FileImageOutputStream(new File(dirImageUsers, CriptografaSenha.criptografar(usuario.getNome()) + "." + EXTENSION));
imageOutput.write(bytes, 0, bytes.length);
imageOutput.flush();
imageOutput.close();
setImage("/resources/img/users/" + CriptografaSenha.criptografar(usuario.getNome()) + "." + EXTENSION);
}
} catch (Exception ex) {
System.out.println("Erro " + ex.getMessage());
}
}

//
// Método que seta o usuário através do carregamento do usuário autenticado (este está sendo pego da session)
//
public void preparaUsuario() {
try {
Usuario usuarioAutenticado = getUsuarioAutenticado().clone();
usuarioAutenticado.setPassword(new String());
setUsuario(usuarioAutenticado);
setExibeUsuarioAutenticado(usuarioAutenticado != null ? true : false);
} catch (Exception ex) {
System.out.println("Erro preparação " + ex.getMessage());
}
}

//
// Cria um thumbnail da imagem com largura máxima de 140. A altura é definida pelo cálculo da
// largura de acordo com o tamanho real da imagem, ou seja, ele redimensiona a imagem por igual
//
public byte[] setThumbnail(byte[] arquivo, String extensao) {
ImageIcon imageIcon = new ImageIcon(arquivo);
Image inImage = imageIcon.getImage();
double scale = (double) 140 / (double) inImage.getWidth(null);

int scaledW = (int) (scale * inImage.getWidth(null));
int scaledH = (int) (scale * inImage.getHeight(null));

BufferedImage outImage = new BufferedImage(scaledW, scaledH, BufferedImage.TYPE_INT_RGB);

AffineTransform tx = new AffineTransform();
if (scale < 1.0d) {
tx.scale(scale, scale);
}

Graphics2D g2d = outImage.createGraphics();
g2d.drawImage(inImage, tx, null);
g2d.dispose();

ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
ImageIO.write(outImage, extensao, baos);
return baos.toByteArray();
} catch (IOException e) {
System.out.println("Error");
} finally {
try {
baos.close();
} catch (IOException e) {
System.out.println("Error");
}
}
return null;
}

public void encerraSessao() {
autenticacaoController.encerraSessao();
}

public Usuario getUsuario() {
return usuario;
}

public void setUsuario(Usuario usuario) {
this.usuario = usuario;
}

public String getMensagem() {
return mensagem;
}

public void setMensagem(String mensagem) {
this.mensagem = mensagem;
}

public void setExibeUsuarioAutenticado(boolean exibeUsuarioAutenticado) {
this.exibeUsuarioAutenticado = exibeUsuarioAutenticado;
}

public boolean isExibeUsuarioAutenticado() {
return exibeUsuarioAutenticado;
}

public boolean isSomenteLeitura() {
return somenteLeitura;
}

public void setSomenteLeitura(boolean somenteLeitura) {
this.somenteLeitura = somenteLeitura;
}

public String getImage() {
return image;
}

public void setImage(String image) {
this.image = image;
}

public UploadedFile getUploadedFile() {
return uploadedFile;
}

public void setUploadedFile(UploadedFile uploadedFile) {
this.uploadedFile = uploadedFile;
}

public Usuario getUsuarioAutenticado() {
FacesContext ctx = FacesContext.getCurrentInstance();
session = (HttpSession) ctx.getExternalContext().getSession(false);
if (session != null) {
usuarioAutenticado = (Usuario) session.getAttribute("usuarioAutenticado");
}
return usuarioAutenticado;
}
}

Pronto, seu ManagedBean está criado, possibilitando realizar o upload da imagem, gravando na base, e posteriormente sendo
recuperada e gerada no servidor.

Agora segue o validator onde limito o tamanho da imagem que o usuário pode enviar para o servidor, evitando assim que o usuário
sobrecarregue o servidor fazendo chamadas desnecessárias.

UsuarioValidator.java


package br.com.pyramides.validator;

import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.ValidatorException;
import javax.inject.Named;
import org.apache.myfaces.custom.fileupload.UploadedFile;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;

/**
*
* @author Lessandro
*/

// Aqui fica a mesma ideia abordada anteriormente no ManagedBean quando utilza ou não injeção de dependências
@Named("usuarioValidator")
@Controller
@Scope("view")
public class UsuarioValidator {

private static final long MAX_FILE_SIZE = 200000; // 200KB

public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
if (((UploadedFile) value).getSize() > MAX_FILE_SIZE) {
FacesMessage facesMsg = new FacesMessage(FacesMessage.SEVERITY_ERROR, new String(), "O arquivo deve ter no máximo 200KB.");
FacesContext.getCurrentInstance().addMessage("iphFileUpload", facesMsg);
throw new ValidatorException(facesMsg);
}
}
}

Pronto, o envio de imagens para o servidor está concluído, espero que tenha ajudado.
Qualquer dúvida basta enviá-la que na medida do possível estarei respondendo.
Abs

agosto 2, 2010

Mensagem de "Um ou mais recursos possuem o destino de ‘head’, mas nenhum componente de ‘head’ foi definido na exibição." no JSF 2.0

Filed under: Desenvolvimento Web — Tags:, , , — semprejava @ 3:47 pm

Olá amigos, bom dia.
Para quem está obtendo essa mensagem “Um ou mais recursos possuem o destino de ‘head’, mas nenhum componente de ‘head’ foi definido na exibição.” no JSF 2.0 deve atentar-se por informar a tag da forma exibida abaixo:

<?xml version='1.0' encoding='UTF-8' ?>
<!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:ui="http://java.sun.com/jsf/facelets"
      xmlns:f="http://java.sun.com/jsf/core">
    <h:head>  <=
            <title>Sistema XXX</title>
    </h:head>
    <h:body>
		<f:view>
			<h:form id="form" prependId="false">
				<h:outputLabel value="xxxx" />
			</h:form>
		</f:view>
	</h:body>
</h:form>            

Se esta tag for informada como “<head>” apenas, esta não é interpretada.
Abs,
Lessandro

agosto 1, 2010

Desenvolvimento de Aplicações WEB para diversos segmentos

Filed under: Desenvolvimento Web — Tags:, , , , — semprejava @ 9:29 pm

Olá pessoal, boa tarde.
Aproveitando para divulgar que desenvolvo aplicações Web para diversos segmentos. Protótipo disponível em: http://lessandronp.homeip.net:8080/AutoPecas
Sistema totalmente desenvolvido utilizando o framework JavaServer Faces + Primefaces 2.1 + Hibernate + EJB + MySQL.
Abraços,
Att,
Lessandro

Blog no WordPress.com.

Crie um site como este com o WordPress.com
Comece agora