Upload File/Image Vraptor3

16 respostas
mdbatera

Bem, pesquisei aqui no forum e encontrei alguns tópicos falando a respeito, mas não consegui aplicar no meu form,

No Modelo Cliente.class, eu adicionei o campo foto como blob e mandei gerar a tabela no banco, confira

package br.com.caelum.produtos.modelo;
import java.io.File;
import java.util.Calendar;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import org.hibernate.validator.Email;
import org.hibernate.validator.NotEmpty;
import org.hibernate.validator.NotNull;
import br.com.caelum.stella.hibernate.validator.CPF;

	@Entity
	public class Cliente {
	
	@Id
	@GeneratedValue
	private int id;
	
	@NotEmpty(message="Favor informar Nome")
	private String nome;
	
	@Column(unique=true)
	@NotEmpty(message="Favor informar CPF")
	@CPF(message="CPF Inválido")
	private String cpf;
	
	@Column(unique=true)
	@NotEmpty(message="Favor informar E-mail")
	@Email(message="E-mail Inválido")
	private String email;
	
	@NotEmpty(message="Favor informar Login")
	private String login;
	
	@NotEmpty(message="Favor informar Senha")
	private String senha;
	
	@NotNull(message="Favor informar Data")
	@Temporal(TemporalType.DATE)
	private Calendar dataNascimento;
	
	@NotEmpty(message="Favor informar Colocar Imagem")
	private File foto;
	
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getNome() {
		return nome;
	}
	public void setNome(String nome) {
		this.nome = nome;
	}
	public String getCpf() {
		return cpf;
	}
	public void setCpf(String cpf) {
		this.cpf = cpf;
	}
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	public String getLogin() {
		return login;
	}
	public void setLogin(String login) {
		this.login = login;
	}
	public String getSenha() {
		return senha;
	}
	public void setSenha(String senha) {
		this.senha = senha;
	}
	public Calendar getDataNascimento() {
		return dataNascimento;
	}
	public void setDataNascimento(Calendar dataNascimento) {
		this.dataNascimento = dataNascimento;
	}
	public void setFoto(File foto) {
		this.foto = foto;
	}
	public File getFoto() {
		return foto;
	}
	
}

clienteDao:

package br.com.caelum.produtos.dao;

import java.util.List;
import org.hibernate.Session;
import org.hibernate.Transaction;
import br.com.caelum.produtos.modelo.Cliente;



public class ClienteDao {
	
	private final Session session;
	
	public ClienteDao(){
		this.session = new HibernateUtil().openSession();
	}
	
	public void adiciona(Cliente c){
		Transaction tx = session.beginTransaction();
		session.save(c);
		tx.commit();
	}
	public void atualiza(Cliente c){
		Transaction tx = session.beginTransaction();
		session.update(c);
		tx.commit();
	}
	public void remove(Cliente c){
		Transaction tx = session.beginTransaction();
		session.delete(c);
		tx.commit();
	}
	@SuppressWarnings("unchecked")
	public List<Cliente> lista(){
		return session.createCriteria(Cliente.class).list();
	}
	public Cliente carrega(Long id) {
		return (Cliente)session.load(Cliente.class,id);		 
	}

}

ClienteController:

package br.com.caelum.produtos.controller;

import java.util.List;
import org.hibernate.exception.ConstraintViolationException;
import br.com.caelum.produtos.dao.ClienteDao;
import br.com.caelum.produtos.modelo.Cliente;
import br.com.caelum.vraptor.Resource;
import br.com.caelum.vraptor.Result;
import br.com.caelum.vraptor.Validator;
import br.com.caelum.vraptor.view.Results;
import static br.com.caelum.vraptor.view.Results.page;



@Resource
public class ClienteController {

	public void cadastracliente(){	
	}
	private Validator validator;
	private Result result;
	
	public ClienteController(Result result, Validator validator){
		this.result = result;
		this.validator = validator;
	}
	
	public List<Cliente> lista(){
		return new ClienteDao().lista();
	}
	

	public void adiciona(Cliente cliente){
		try{
		
		validator.validate(cliente); 
		validator.onErrorUse(page()).of(ClienteController.class).cadastracliente();
		new ClienteDao().adiciona(cliente);
		result.include("mensagem", "Cliente cadastrado com sucesso");
		result.use(Results.logic()).redirectTo(ClienteController.class).cadastracliente();						
		}catch (ConstraintViolationException e) {
			result.include("mensagem", "Usuário já registrado");
			result.use(Results.logic()).redirectTo(ClienteController.class).cadastracliente();
		}
		
	}
	public void atualiza(Cliente cliente){
		validator.validate(cliente); 
		validator.onErrorUse(page()).of(ClienteController.class).cadastracliente();
		new ClienteDao().atualiza(cliente);
		result.include("mensagem", "Cliente cadastrado com sucesso");
		result.use(Results.logic()).redirectTo(ClienteController.class).cadastracliente();
		
	}
	public void remove(Cliente cliente){
		validator.validate(cliente); 
		validator.onErrorUse(page()).of(ClienteController.class).cadastracliente();
		new ClienteDao().remove(cliente);
		result.include("mensagem", "Cliente cadastrado com sucesso");
		result.use(Results.logic()).redirectTo(ClienteController.class).cadastracliente();
		
	}
}

No Jsp, coloquei o campo:

Foto: <input type="file" name="cliente.foto" value="${cliente.foto}"/><br/>

O que penso é que era pra ele já está enviando aquivo para o banco de dados, mas não está enviando.

Na documentação do vraptor ele não me diz se aplico a função no Controller ou no Dao, poderiam me da uma luz?

Valeuuu

16 Respostas

G
<form action="sua-action-aqui" method="post" enctype="multipart/form-data">
 seu-form-aqui
</form>
mdbatera

no form, já tem a action “adiciona”, no caso colocaria a action de enviar dentro da action “adiciona”?

Guevara

Oi amigo!
Como vc captura na JSP os erros de validação? Da forma clássica mesmo?Reparei que vc valida anotando a classe. =)

<ul>
		<c:forEach var="error" items="${errors}">   
    		<li>${error.message}</li>   
		</c:forEach>  
		</ul>

Na action do form vc coloca apenas “adiciona” que é o nome do método que está no seu controller.

Abraço!

mdbatera

Opa!,

Bem acredito que eu não esteja usando boas praticas hehehe, mas uso uma div para mensagem da classe e outra pra mensagens do hibernate/vraptor.

esse metodo envia a mensagem após ação: result.include("mensagem", "Usuário  registrado");

esse imprime a mensagem no jsp:
<div >
   ${mensagem}
</div>


Validações do hibernate com anotations:
  
Esse pega erros de validação do hibernate:
       <c:forEach var="error" items="${errors}">  
             <li><label class="erro">${error.message}</label></li>  
       </c:forEach>

Aí, quando adiciono o cliente, se der algum erro no registro do banco ele usa o forEach, e se ele executar a ação normalmente ele usa a DIV.

Espero ter ajudado!

G

Não, você deve usar esse form que eu te passei, apenas alterando o endereço da tua action. Não ví teu JSP, mas pelo que noto dos erros rotineiros do pessoal no GUJ, sempre esquecem de colocar o enctype.

mdbatera

Voltando…

Já com o File foto anotado no Modelo(Cliente), input no jsp “file” e a função adiciona prontinha, queria saber como é que eu adiciono a rotina de inclusão do arquivo no Banco de Dados.

Ta aí o metodo que joga as informações no banco, a rotina de enviar aquivo pro banco entra aqui nesse metodo?

public void adiciona(Cliente cliente){
		try{
		validator.validate(cliente); 
		validator.onErrorUse(page()).of(ClienteController.class).cadastracliente();
		new ClienteDao().adiciona(cliente);
		result.include("mensagem", "Cliente cadastrado com sucesso");
		result.use(Results.logic()).redirectTo(ClienteController.class).cadastracliente();						
		}catch (ConstraintViolationException e) {
			result.include("mensagem", "Usuário já registrado");
			result.use(Results.logic()).redirectTo(ClienteController.class).cadastracliente();
		}
		
	}

Obrigadoww!

Guevara

Valeu mdBatera, só pra saber se tava funcionando. Não achei a abordagem ruim não, já vi gente fazendo a validação na classe. =)

Garcia.
Eu me referi a colocar o método adiciona para fazer upload:

<form action="adiciona" method="post" enctype="multipart/form-data">
 seu-form-aqui
</form>

Depois que eu reparei q é o método para salvar o Cliente, que por sua vez possui campo Blob.
No fórum eu já vi método para salvar esse tipo de campo.
Desculpa a confusão. =)

mdbatera

Bem segui como você falou eu com meu “newbaismo” não sabia deste enctype.

Modifiquei e deu um errinho:

javax.servlet.ServletException: Filter execution threw an exception

root cause

java.lang.NoClassDefFoundError: org/apache/commons/io/output/DeferredFileOutputStream
	org.apache.commons.fileupload.disk.DiskFileItemFactory.createItem(DiskFileItemFactory.java:196)
	org.apache.commons.fileupload.FileUploadBase.parseRequest(FileUploadBase.java:358)
	org.apache.commons.fileupload.servlet.ServletFileUpload.parseRequest(ServletFileUpload.java:126)
	br.com.caelum.vraptor.interceptor.multipart.MultipartInterceptor.intercept(MultipartInterceptor.java:90)
	br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
	br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:65)
	br.com.caelum.vraptor.core.DefaultRequestExecution.execute(DefaultRequestExecution.java:70)
	br.com.caelum.vraptor.VRaptor$1.insideRequest(VRaptor.java:92)
	br.com.caelum.vraptor.ioc.spring.SpringProvider.provideForRequest(SpringProvider.java:56)
	br.com.caelum.vraptor.VRaptor.doFilter(VRaptor.java:89)

root cause

java.lang.ClassNotFoundException: org.apache.commons.io.output.DeferredFileOutputStream
	org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1387)
	org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1233)
	org.apache.commons.fileupload.disk.DiskFileItemFactory.createItem(DiskFileItemFactory.java:196)
	org.apache.commons.fileupload.FileUploadBase.parseRequest(FileUploadBase.java:358)
	org.apache.commons.fileupload.servlet.ServletFileUpload.parseRequest(ServletFileUpload.java:126)
	br.com.caelum.vraptor.interceptor.multipart.MultipartInterceptor.intercept(MultipartInterceptor.java:90)
	br.com.caelum.vraptor.core.ToInstantiateInterceptorHandler.execute(ToInstantiateInterceptorHandler.java:54)
	br.com.caelum.vraptor.core.DefaultInterceptorStack.next(DefaultInterceptorStack.java:65)
	br.com.caelum.vraptor.core.DefaultRequestExecution.execute(DefaultRequestExecution.java:70)
	br.com.caelum.vraptor.VRaptor$1.insideRequest(VRaptor.java:92)
	br.com.caelum.vraptor.ioc.spring.SpringProvider.provideForRequest(SpringProvider.java:56)
	br.com.caelum.vraptor.VRaptor.doFilter(VRaptor.java:89)

Só pra constar a JSP:

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib tagdir="/WEB-INF/tags" prefix="formtag"%>

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Cadastro de Cliente</title>
<script type="text/javascript" src="<c:url value="/js/jquery.js"/>"></script>
<script type="text/javascript" src="<c:url value="/js/jquery-ui.js"/>"></script>
<link type="text/css" href="<c:url  value="/css/jquery.css"/>" rel="stylesheet" />
</head>
<body>

<form action="<c:url value="/cliente/adiciona"/>" method="post" enctype="multipart/form-data">


Nome: <input name="cliente.nome" value="${cliente.nome}"/><br/>
Cpf: <input name="cliente.cpf" value="${cliente.cpf}"/><br/>
Email: <input name="cliente.email" value="${cliente.email}"/><br/>
Data: <formtag:campoData name="cliente.dataNascimento" id="dataNascimento" /><br/>

Login: <input name="cliente.login" value="${cliente.login}"/><br/>
Senha: <input type="password" name="cliente.senha" value="${cliente.senha}"/><br/>
Foto: <input type="file" name="cliente.foto" value="${cliente.foto}"/><br/>

<input type="submit" />

</form>
<div >
   ${mensagem}
</div>
<ul>  
       <c:forEach var="error" items="${errors}">  
             <li><label class="erro">${error.message}</label></li>  
       </c:forEach>  
 </ul> 
</body>
</html>

Acho que ta faltando besteira!!

Valeu!

G

Você deve SEMPRE ler as mensagens de erros.

Vocẽ precisa ter o jar commons-io. Alías todas as dependências do vraptor estão no download dele, do diretório /lib/mandatory.

mdbatera

Realmente, estava sem o io, adicionei o jar, removi a anotation de NotEmpty e ele cadastra no banco beleza, só que em vez de enviar o arquivo, aparece [BLOB - 0Bytes], será que falta algum metodo?

G

Provavelmente falta você gravar as informações dentro desse blob.

O Vraptor te retorna um objeto UploadFile (ou algo assim), sendo que o método getFile te retorna um InputStream. Usando o org.apache.commons.io.IOUtils.toByteArray(java.ui.InputStream) você pode obter um array de bytes para gravar nesse campo blob, assim o JPA salva para você o conteúdo do arquivo.

O JPA não consegue trabalhar direto com File, você precisa usar byte ao invés de File. Troque isso:

private File foto;
Por isso:

Assim o JPA vai serializar o conteúdo da foto para o banco de dados.

Além disso deixo duas obversações/dicas: sempre feche os streams, e veja se você não pode deixar o atributo de foto do cliente em outra classe, pois se você deixar na mesma tabela que os outros dados dependendo do tamanho do seu sistema pode ficar muito lento.

Guevara

Pra mostrar o Blob da JSP têm esta dica do Lucas:
http://groups.google.com/group/caelum-vraptor/msg/e2a3a93bfaf52e2c?pli=1
Caso alguém precise fazer upload gravando campo Blob e mostrar na JSP o tópico fica completo para ajudar outra pessoa. =)

G

Guevara:
Pra mostrar o Blob da JSP têm esta dica do Lucas:
http://groups.google.com/group/caelum-vraptor/msg/e2a3a93bfaf52e2c?pli=1
Caso alguém precise fazer upload gravando campo Blob e mostrar na JSP o tópico fica completo para ajudar outra pessoa. =)

Você estava na volta disso outro dia, não é? Não é esse o tópico? http://www.guj.com.br/posts/list/204564.java

Guevara

Vc viu o final do tópico Garcia? :lol:
Tá resolvido, só que eu não uso Blob, salvo numa pasta e gravo o nome, descrição e nome das fotos no banco. Fiz relacionamento @OneToMany entre imóvel e fotos, o que poderia ser feito pelo mdBatera tb.

mdbatera

Rapaz estive analisando aqui, e decidi que quero fazer exatamente isso!

Pelo o que garcia me falou não é bom eu colocar a imagem na mesma tabela do cadastro, ai faria outra tabela com id e o caminho da imagem e cadastraria o id da imagem com o id do cadastro não é isso?

Meu problema é, não tenho ideia de qual função e aonde chamar o arquivo, se é no controller ou criar um novo pacote ou nova classe no controller “upload”, estou perdido.

Vou dar uma olhada no tópico de guevara aí!

Valeu!!

G
Guevara:
Vc viu o final do tópico Garcia? :lol:

Se tu estás falando do ònibus, ví sim. Curto um monte carros/ônibus antigos. Quando faço o trajeto Porto Alegre / Caxias do Sul tem um lugar que tem dois daqueles rabecão dos caça fantasmas. Um dia desses quero passar lá para namorar um pouco os modelos, hahah. O modelo é um Cadilac Eldorado Ambulance.

mdbatera:
Pelo o que garcia me falou não é bom eu colocar a imagem na mesma tabela do cadastro, ai faria outra tabela com id e o caminho da imagem e cadastraria o id da imagem com o id do cadastro não é isso?

Vocẽ precisará mudar pouca coisa. O que eu faço é ter uma classe chamada BinaryFile que tem um id, o conteúdo do arquivo, contenttype, etc.

public class BinaryFile {

    @Id
    @GeneratedValue
    private Long id;

    private String name; //nome do arquivo

    @Lob
    private byte[] content; //conteúdo do arquivo

    @Temporal(TemporalType.TIMESTAMP)
    private Date creationDate; // data da criação

    private String contentType; // cabeçalho mime do arquivo

    [...]
}

Depois você relaciona o BinaryFile como OneToOne com o cliente:

@OneToOne(cascade=CascadeType.ALL, deleteOrphan=true)
private BinaryFile fotoDoCliente;

Para você fazer o upload pode modificar seu controller para isso aqui:

public void restore(UploadedFile file)  {
        InputStream input = file.getFile(); // abre o stream para ler o arquivo
        byte[] content = IOUtils.toByteArray(input); // lê o conteúdo
        IOUtils.closeQuietly(input); // fecha o stream

        BinaryFile bfile = new BinaryFile();
        bfile.setName(file.getFilename());
        bfile.setContentType(file.getContentType());
        bfile.setCreationDate(new Date());
        bfile.setContent(content);

        Cliente cliente = [...]
        cliente.setFoto(bfile);

        [...]
    }
Criado 26 de maio de 2010
Ultima resposta 27 de mai. de 2010
Respostas 16
Participantes 3