Fala galeraaa, tudo bem?

Após algum tempo ai com o blog parado, estou voltando a ativa e hoje com um post interessante.
Vejo muita gente pedindo, inclusive nos comentários do blog, e possuindo muitas duvidas de como fazer.

No post de hoje veremos como fazer um cadastro de produtos que possui uma foto. Esta foto será guardada em um diretorio dentro do computador, posteriormente iremos exibir esta foto quando o usuario requisita-la.

Tanto o código fonte como o projeto está disponivel ao fim do post para download.

O projeto terá esta cara ao final do post:

imagem-contexto

Vamos lá?

O objetivo do post será o upload e a requisição da imagem pelo usuário, então não darei enfase nos cadastro.

Neste post estarei utilizando o maven para fazer o gerenciamento de minhas bibliotecas e building ( link para instalação/configuração: Maven ).

O pom.xml ( Principal arquivo de configuração de uma aplicação com recursos maven, o mesmo faz a gerência das dependencias do projeto, o ciclo de vida e as etapas para construção do mesmo ) ficou da seguinte forma:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.programandojava</groupId>
	<artifactId>produto</artifactId>
	<version>0.0.1</version>
	<packaging>war</packaging>
	<name>produto</name>
	<description>Aplicação disponibilizada no blog programandojava.wordpress.com com intuito de mostrar como exibir imagem no contexto com JSF 2.2</description>
	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.1</version>
				<configuration>
					<source>1.7</source>
					<target>1.7</target>
				</configuration>
			</plugin>
		</plugins>
		<finalName>produto</finalName>
	</build>
	<repositories>
		<repository>
			<id>primefaces-repository</id>
			<name>PrimeFaces Maven Repository</name>
			<url>http://repository.primefaces.org</url>
			<layout>default</layout>
		</repository>
	</repositories>
	<dependencies>
		<dependency>
			<groupId>com.sun.faces</groupId>
			<artifactId>jsf-api</artifactId>
			<version>2.2.3</version>
		</dependency>
		<dependency>
			<groupId>com.sun.faces</groupId>
			<artifactId>jsf-impl</artifactId>
			<version>2.2.3</version>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>3.1.0</version>
		</dependency>
		<dependency>
			<groupId>org.primefaces</groupId>
			<artifactId>primefaces</artifactId>
			<version>4.0</version>
		</dependency>
		<dependency>
			<groupId>org.primefaces.themes</groupId>
			<artifactId>flick</artifactId>
			<version>1.0.10</version>
		</dependency>
		<dependency>
			<groupId>commons-fileupload</groupId>
			<artifactId>commons-fileupload</artifactId>
			<version>1.3</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>1.7.5</version>
		</dependency>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-core</artifactId>
			<version>3.6.10.Final</version>
		</dependency>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-entitymanager</artifactId>
			<version>3.6.10.Final</version>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.26</version>
		</dependency>
	</dependencies>
</project>

As entitdades do projeto:

@Entity
@Table(name = "TBL_PRODUTO")
public class Produto implements Serializable {
	private static final long serialVersionUID = -834851092508638127L;

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "PRODUTO_ID")
	private Long id;

	@OneToOne
	@JoinColumn(name = "FOTO_ID")
	@Cascade(value = CascadeType.ALL)
	private Foto foto;

	@Column(name = "PRODUTO_NOME")
	private String nome;

	// GETTERS / SETTERS
@Entity
@Table(name = "TBL_FOTO")
public class Foto implements Serializable {
	private static final long serialVersionUID = -2714493688597274989L;

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "FOTO_ID")
	private Long id;

	@Column(name = "FOTO_NOME")
	private String name;

	public Foto(String name) {
		this.name = name;
	}

	public Foto() { }

	// GETTERS / SETTERS

o DAO do projeto:

public class ProdutoDAO implements Serializable {
	private static final long serialVersionUID = -8824478295052952158L;

	public Produto save(Produto produto) {
		EntityManager entityManager = Util.getEntityManager();

		return entityManager.merge(produto);
	}

	public List<Produto> listAll() {
		CriteriaQuery<Produto> query = Util.getEntityManager().getCriteriaBuilder().createQuery(Produto.class);

		return Util.getEntityManager().createQuery(query.select(query.from(Produto.class))).getResultList();
	}
}

Controller:

@ManagedBean(name = "produtoMB")
@ViewScoped
public class ProdutoController implements Serializable {
	private static final long serialVersionUID = -4131203131879586886L;

	@ManagedProperty(value = "#{uploadMB}")
	private UploadController uploadController;

	private Produto produto;

	private Produto produtoCarregado;

	private List<Produto> produtos;

	private ProdutoDAO dao;

	@PostConstruct
	public void init() {
		if(this.dao == null)
			this.dao = new ProdutoDAO();

		if(this.produto == null)
			this.produto = new Produto();

		if(this.produtoCarregado == null)
			this.produtoCarregado = new Produto();

		this.produtos = this.dao.listAll();
	}

	public void salvar(){
		this.produto.setFoto(FotoUtil.handlePicture(this.uploadController.getImageBytes()));

		this.dao.save(this.produto);

		this.produtos.add(this.produto);

		FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Cadastro", "Produto inserido com êxito !"));

		this.produto = new Produto();
	}

	public void carregarProduto(Produto produto){
		this.produtoCarregado = produto;
	}
@ManagedBean(name = "uploadMB")
@ViewScoped
public class UploadController implements Serializable {
	private static final long serialVersionUID = 7475247398584602545L;

	private byte[] imageBytes;

	public void handlePictureUploaded(FileUploadEvent uploadEvent){
		this.imageBytes = uploadEvent.getFile().getContents();
	}
ManagedBean(name = "fotoMB")
@RequestScoped
public class FotoController implements Serializable {
	private static final long serialVersionUID = -2512806101450140965L;

	public StreamedContent getFoto() throws FileNotFoundException {
		String fotoNome = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("fotoNome");

		if(FacesContext.getCurrentInstance().getRenderResponse() || fotoNome == null)
			return new DefaultStreamedContent();

		else
			return FotoUtil.recuperarFotoDisco(fotoNome);
	}
}

fotoNome (LINHA: 7):

Variavel responsável por receber o nome da imagem do produto que o usuário requisito.

Linhas 9, 10 e 13:

A especificação do JSF, define 3 tipos de requisições:

  • 1. Uma requisição JSF que gera Resposta JSF
  • 2. Um requisição não JSF que gera uma Resposta JSF
  • 3. Uma requisição JSF que gera uma Resposta não JSF

Requisição e Resposta JSF são feitas e especificadas pelo framework.
Requisição e Resposta não JSF estão fora do escopo do framework.

Segundo o ciclo de vida JSF, o cliente JSF faz uma requisição JSF, que gera uma resposta JSF.
Se for a primeira requisição da página, a requisição passará pela fase de Restore View retornando
a página imediatamente. Contudo, se já houve a passagem por essa fase ao menos uma vez, a requisição alcançara a fase de Render Response, o que estamos querendo saber é exatamente isso na lina 10, se a requisição passará por Render Response.

A especificação deixa claro que a view do cliente JSF, deverá gerar uma requisição não JSF e uma Resposta JSF ou não JSF para imagens. Ou seja, a aplicação envia a view ao cliente, o cliente requisita as imagens ao servidor quando necessário. Podemos notar que isso é uma técnica de lazy load de imagens. Nesse caso o navegador é responsável por fazer a requisição, por isso chamamos de uma requisição não JSF.

As bibliotecas de lazy load image diponíveis em javascript, postergam ou atrasam a definição do atributo src da tag image, isso é exatamente o que JSF faz em background.

A classe util do projeto:

public class FotoUtil implements Serializable {
	private static final long serialVersionUID = -7538592532189320120L;

	private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("dd-MM-yyyy-HH-mm-ss-SSS");
	public static final String FOLDER = File.separator + "programandoJava" + File.separator;

	public static Foto handlePicture(byte[] imageToHandle) {
		String nomeFoto = DATE_FORMAT.format(Calendar.getInstance().getTime()) + ".png";

		Foto foto = new Foto(nomeFoto);

		try {
			salvarNoDisco(ImageIO.read(new ByteArrayInputStream(imageToHandle)), nomeFoto);
		} catch (IOException e) {
			e.printStackTrace();
		}

		return foto;
	}

	private static void salvarNoDisco(BufferedImage fotoSalvar, String nomeFoto) throws IOException{
		File f = new File(FOLDER);

		if(!f.exists()){
			f.mkdirs(); f.setReadable(true); f.setWritable(true);
		}

		ImageIO.write(fotoSalvar, "png", new File(FOLDER + nomeFoto));
	}

	public static StreamedContent recuperarFotoDisco(String fotoNome) throws FileNotFoundException{
		try {
			return new DefaultStreamedContent(new FileInputStream(new File(FOLDER + fotoNome)), "image/png");
		} catch (IOException e) {
			e.printStackTrace();

			return new DefaultStreamedContent(new FileInputStream(new File(FOLDER + "DEFAULT.png")), "image/png");
		}
	}
}

Pagina 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:ui="http://java.sun.com/jsf/facelets"
	xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core"
	xmlns:p="http://primefaces.org/ui">

<h:head>
	<f:facet name="first">
			<meta http-equiv="X-UA-Compatible" content="EmulateIE8" />
			<meta content='text/html; charset=UTF-8' http-equiv="Content-Type" />
		</f:facet>

		<title>Cadastro com foto no contexto | ProgramandoJava :)</title>

		<link rel="stylesheet" type="text/css" href="#{facesContext.externalContext.requestContextPath}/assets/css/style.css" />
</h:head>

<h:body>
	<h:form enctype="multipart/form-data" id="frmProduto">
		<p:growl showSummary="false" showDetail="true" />

		<p:panel footer="ProgramandoJava . wordpress.com" header="Cadastro com foto no contexto - Produto" toggleable="true">
			<h:panelGrid columns="2" cellpadding="2" cellspacing="2">
				<h:outputLabel for="txtProdutoNome" value="Nome: " style="float: right; font-weight: bold; "/>
				<p:inputText id="txtProdutoNome" value="#{produtoMB.produto.nome}" size="50" required="true" requiredMessage="Campo [Nome] é obrigatório" />

				<h:outputLabel for="fuProdutoFoto" value="Foto: " style="float: right; font-weight: bold; " />
				<p:fileUpload id="fuProdutoFoto" allowTypes="/(\.|\/)(jpe?g|png)$/" cancelLabel="Cancelar"
					label="Escolha" invalidFileMessage="Extensão de arquivo não suportada"
					invalidSizeMessage="Imagem excedeu o tamanho permitido" mode="advanced" multiple="false"
					showButtons="true" sizeLimit="2097152" uploadLabel="Enviar"
					fileUploadListener="#{uploadMB.handlePictureUploaded}" required="true" requiredMessage="Campo [Foto] é obrigatório" />

				<p:spacer />
				<p:commandButton value="Salvar" action="#{produtoMB.salvar()}" icon="ui-icon-circle-plus" update="frmProduto, :frmDt" />
			</h:panelGrid>
		</p:panel>
	</h:form>

	<h:form id="frmDt">
		<p:panel footer="ProgramandoJava . wordpress.com" header="Dados cadastrados" toggleable="true">
			<p:dataTable id="produtoDT" var="prod" emptyMessage="Nenhum registro encontrado"
					stickyHeader="true" value="#{produtoMB.produtos}" style="text-align: center;"
					paginator="true" rows="5" paginatorTemplate="{CurrentPageReport}  {FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}"
	               	rowsPerPageTemplate="5,10,15">

				<f:facet name="header">
					Cadastro de produtos com fotos no contexto
				</f:facet>

				<p:column headerText="Id do produto">
					<h:outputText value="#{prod.id}" />
				</p:column>

				<p:column headerText="Nome do produto">
					<h:outputText value="#{prod.nome}" />
				</p:column>

				<p:column headerText="Visualizar foto">
					<p:commandButton icon="ui-icon-circle-zoomin" action="#{produtoMB.carregarProduto(prod)}" oncomplete="PF('dlgFoto').show();" update=":dlgFotoId" />
				</p:column>

				<f:facet name="footer">
					programandoJava . wordpress.com
				</f:facet>
			</p:dataTable>
		</p:panel>
	</h:form>

	<p:dialog appendTo="@(body)" closable="true" closeOnEscape="true"
		draggable="true" id="dlgFotoId" modal="true" position="center"
		resizable="false" widgetVar="dlgFoto" header="Foto do produto">

		<h:form id="forImages">
			<p:graphicImage value="#{fotoMB.foto}" width="800px" height="400px">
				<f:param name="fotoNome" value="#{produtoMB.produtoCarregado.foto.name}" />
			</p:graphicImage>
		</h:form>
	</p:dialog>
</h:body>
</html>

E é isso galera, chega o fim mais um post aqui do blog ..

Disponibilização do projeto:

Anúncios