Método para deletar imagem da pasta [Resolvido]

26 respostas
Guevara

Oi pessoal!
Tenho a url das imagens no banco e preciso que ao deletar os dados do imóvel e das fotos, as imagens da pasta tb sejam deletadas:

O método que faz a remoção do imóvel é este:

public void remove(Long idImovel) {
		Imovel imovel = imovelDao.carrega(idImovel);
		imovelDao.remove(imovel);
		imagens.deleta(imovel); // aqui envia o imovel para o componente Imagens e assim deletar a imagem da pasta
		// mensagem de Imovel removido com sucesso
		result.include("mensagem", "Imovel removido com sucesso");
		// redirecionamento para a listagem de imoveis
		result.use(Results.logic()).redirectTo(ImovelController.class).lista();
	}

O método deletar do componente:

public void deleta(Imovel imovel) {
		
                // Preciso de um código para recuperar as URL's das imagens que estão no banco e deletar conforme abaixo
                // Talvez precise de uma List de objetos fotos		
		File destino = new File(pastaImagens, urlImagem);
		try {
			 destino.delete();
		} catch (Exception e) {
			throw new RuntimeException("Erro ao deletar imagem", e);
		}
		
	}

O miolo ali no método deleta que eu não estou conseguindo fazer, já que a única coisa que é enviada para o método deleta é “imovel”, precisaria na verdade das URL’s das imagens que estão no banco, sendo que ao deletar o imóvel os dados na tabela foto tb são deletados automáticamente, ai fico sem o caminho para deletar as imagens que estão na pasta.
Tentei algo do tipo:

public void deleta(Imovel imovel) {
File destino = new File(pastaImagens, imovel.getIdImovel() +  "." + foto.getNome() + "." + imagem.getFileName());
try {
	destino.delete();
    } catch (Exception e) {
	throw new RuntimeException("Erro ao deletar imagem", e);
   }
}

Obviamente não vai funcionar nunca, pois somente o imóvel é passado como parãmetro, faltando o resto.
Alguém pode dar uma dica?
Abraço!!

26 Respostas

igor_jua

Não dá pra vc deletar primeiro as imagens da pasta e depois partir pra deleção no banco?

Lucas_Cavalcanti

deleta a imagem primeiro, e deixa um getter dentro do Imovel pra pegar o File da imagem … não precisa estar no banco…

G

Embora eu nunca trabalhe com acesso ao filesystem, acho errado você excluir primeiro a imagem do filesystem e depois do banco. Afinal, se der algum problema na base você faz rollback na base e o registor continua lá, porém a imagem já foi excluída do filesystem, e assim você fica com a base de dados quebrada.

Se você excluir primeiro o registro no banco e depois a imagem no filesystem, caso der erro ao excluir o registro a imagem e o registro permanecem no banco e integros. E se der erro ao excluir a mensagem, a transação não será efetivada (desde que você trate) e o registro permanecerá lá no banco, assim como a imagem.

No caso do problema do colega Guevara, você deve primeiro armazenar em alguma variável os dados da imagem que você quer apagar, depois excluir o registro. Se você não entender, me avise.

Guevara

Oi pessoal!
Lucas, o problema que eu estou encontrando é como implementar esse método de remoção, quanto a ordem de remoção isso poderia ser tratado numa estrutura try/catch checando se a id do imovel bate com a fk do imovel na tabela Foto e se estiver no banco mandar deletar tanto da pasta como do banco.
Algo do tipo:

public void remove(Long idImovel) throws Exception {
		try {
			if (foto.getImovel().equals(idImovel)) {
				Imovel imovel = imovelDao.carrega(idImovel);
				imovelDao.remove(imovel);
				imagens.deleta(imovel);
			}
		} catch (Exception e) {
			throw new Exception("erro ao deletar imovel", e);
		}
		// mensagem de Imovel removido com sucesso
		result.include("mensagem", "Imovel removido com sucesso");
		// redirecionamento para a listagem de imoveis
		result.use(Results.logic()).redirectTo(ImovelController.class).lista();
	}

Mas preciso do método “deleta” lá no componente Imagens. =/
Se alguém, tiver uma idéia melhor do que essa agradeço a colaboração. =)
Garcia, para resolver esse ponto que vc citou a estrutura que eu montei logo acima não resolveria?
O que mais me preocupa é o método pra deletar, pois preciso deletar todas as imagens com a id do imóvel, sendo que para o componente Imagens só é passado o parâmetro imóvel. =/
Abraço!!

Lucas_Cavalcanti

se o Imagens é um @Component é só receber o dao de imagens no construtor

Guevara

Pois é Lucas, eu fiz isso, só têm um probleminha na FotoDAO, é que para recuperar a url das fotos a ID do imovel é uma FK, não posso pegar usando esta criteria:

public String getUrlFoto(Long idImovel) {
		Criteria c = this.session.createCriteria(Foto.class);
		c.add(Restrictions.eq("idImovel", idImovel));
		c.setProjection(Projections.max("urlFoto"));
		return (String) c.uniqueResult();
	}

Precisaria de um:

select url_foto FROM foto WHERE imovel_id_imovel = id_imovel

Conseguindo montar essa criteria mandaria o resultado para o componente injetando no construtor e o método deleta teóricamente funcionaria, permitindo assim o método “remove” do ImovelController funcionar tb.
Estou no caminho certo ou existe outra forma de fazer isto?

Lucas_Cavalcanti

Vc tem um relacionamento configurado com @ManyToOne ou @OneToOne?

se sim, vc pode fazer a restriction:

Restrictions.add("imovel.id", idImovel)

ou trocar tudo pra HQL (que eu acho mais fácil e melhor)

return session.createQuery("select f.urlFoto from Foto f where f.imovel.id = :id")
     .setParameter("id", idImovel)
     .uniqueResult();
Guevara

Não deu boa Lucas. =/
O método de listar está retornando null pro FotoDAO, ai fiz de uma forma diferente, carrego um objeto foto e mando para o componente:

public void remove(Long idImovel) {				
		Imovel imovel = imovelDao.carrega(idImovel);
		Foto foto = fotoDAO.carrega(idImovel);
		imagens.deleta(foto);		
		imovelDao.remove(imovel);	
		// mensagem de Imovel removido com sucesso
		result.include("mensagem", "Imovel removido com sucesso");
		// redirecionamento para a listagem de imoveis
		result.use(Results.logic()).redirectTo(ImovelController.class).lista();
	}

No componente extraio a url e armazeno numa variável “caminho”:

public void deleta(Foto foto) {	
		String caminho = foto.getUrlFoto();
		File destino = new File(pastaImagens, caminho );		
		try {
			 destino.delete();
		} catch (Exception e) {
			throw new RuntimeException("Erro ao deletar imagem", e);
		}
	}

Agora, pra isso funcionar preciso carregar um objeto foto pela fk e não pela id de foto e sim pelo id de imóvel:

public Foto carrega(Long idImovel) {
	    return (Foto) this.session.load(Foto.class, idImovel); // aqui é que está errado, teria que carregar foto pela fk imovel_id_imovel.
  }

Parece que se esse método “carrega” funcionar, o resto vai funcionar. =)

Lucas_Cavalcanti

ussa isso no seu método carrega:

return  session.createQuery("select f.urlFoto from Foto f where f.imovel.id = :id")  
         .setParameter("id", idImovel)  
         .uniqueResult();

vc só precisa do relacionamento configurado

Guevara

Deu certo Lucas, mas com esse parâmetro .uniqueResult(); só apaga uma foto da pasta? E se tiver mais fotos lá?
Não pude testar isso pq o displaytag tá listando errado, tenho um imóvel com três fotos, no banco está correto e na pasta tb, mas na hora de listar o displaytag tá listando três imóveis, quando na verdade existe apenas um. Creio que seja pelo fato de estar pegando as fotos junto, ai ele entende três imóveis. =/

Lucas_Cavalcanti

se um imóvel tem várias fotos é só trocar uniqueResult() por list() e apagar todas

Guevara

Lucas, o método só consegue apagar um imagem:

org.hibernate.NonUniqueResultException: query did not return a unique result: 5

Esse método que vc passou vou aproveitar para deletar uma imagem apenas, mas para deletar todas preciso de todas as url’s de foto do imóvel, fiz as alterações:

public List<Foto> getFotos(Long idImovel) {
		return session.createQuery("select f.urlFoto from Foto f where f.imovel.id = :id")  
        .setParameter("id", idImovel).list();
	}
public void remove(Long idImovel) {				
		Imovel imovel = imovelDao.carrega(idImovel);
		//Remove as fotos do imovel
		//String caminho = fotoDAO.getUrlFoto(idImovel);
		List<Foto> caminho = fotoDAO.getFotos(idImovel);
		imagens.deleta(caminho);
		imovelDao.remove(imovel);	
}

Agora, no método deleta lascou:

public void deleta(List<Foto> caminho) {
		File destino = new File(pastaImagens, caminho );		
		try {
			 destino.delete();
		} catch (Exception e) {
			throw new RuntimeException("Erro ao deletar imagem", e);
		}
	}

O “caminho” não pode ser instanciado sendo que ele agora é uma Lista de url. =/

G

Guevara:
Agora, no método deleta lascou:

public void deleta(List<Foto> caminho) {
		File destino = new File(pastaImagens, caminho );		
		try {
			 destino.delete();
		} catch (Exception e) {
			throw new RuntimeException("Erro ao deletar imagem", e);
		}
	}

O “caminho” não pode ser instanciado sendo que ele agora é uma Lista de url. =/

public void deleta(List<Foto> caminhos) { for(String caminho: caminhos) { File destino = new File(pastaImagens, caminho ); try { destino.delete(); } catch (Exception e) { throw new RuntimeException("Erro ao deletar imagem", e); } } }

Guevara

Oi Garcia!
Na linha:

for(String caminho: caminhos)

Está mandando renomear o caminho para tipo Foto:

for(Foto caminho: caminhos)

Se renomear dá zica na linha para instanciar o caminho.

G

Guevara, desculpe, acabei me perdendo. O correto é usar Foto ao invés de String, já que você tem List de Foto.

Mas a sua IDE deveria reclamar disso e corrigir, não?

public void deleta(List<Foto> fotos) { for(Foto foto: fotos) { File destino = new File(pastaImagens, foto.getCaminho()); // ver aqui como pega o caminho try { destino.delete(); } catch (Exception e) { throw new RuntimeException("Erro ao deletar imagem", e); } } }

Guevara

Pois é, a IDE não está me dando a opção de converter essa lista, agora ao rodar aparece o erro:

java.lang.ClassCastException: java.lang.String cannot be cast to br.com.imobiliaria.bean.Foto
	br.com.imobiliaria.component.Imagens.deleta(Imagens.java:54)

É justamente nesta linha:

for(Foto foto: fotos)
G

Guevara, eu não tenho muito como adivinhar/descobrir quais são os tipos corretos. Mas isso você pode fazer.

O segredo aqui é que você tem uma lista de fotos, correto? Como para você excluir você precisa ter o caminho da foto, então você precisa percorrer a lista de fotos, pegar o caminho da imagem e excluir a foto.

Se você está recebendo List como argumento não há como ter esse class-cast.

G

Hmm, agora entendi… você está fazendo o return type do método da DAO errado.

public List<Foto> getFotos(Long idImovel) { return session.createQuery("select f.urlFoto from Foto f where f.imovel.id = :id") .setParameter("id", idImovel).list(); }

Você está fazendo um select em urlFoto que é String, porém você mascara isso como List. Altere seu dao para List e propague as alterações para o método Imagens.deleta.

Guevara

Achei esta dica com o Iterator, creio que esta é a forma de fazer:
http://www.javapractices.com/topic/TopicAction.do?Id=125
Implementei aqui:

public void deleta(List<Foto> fotos) {
			for(Iterator<String> it = fotos.iterator(); it.hasNext();) {
			File destino = new File(pastaImagens, it.hasNext());
			try {
				 destino.delete();
			} catch (Exception e) {
				throw new RuntimeException("Erro ao deletar imagem", e);
			}
		}
	
	}

Mas não está aceitando, diz que o Iterator não é genérico, não entendi essa, se tenho url como String deveria aceitar não?
Pelo menos, as url estão em “fotos”.

Agora que eu vi seu último post, vou lá conferir no DAO

Guevara

Fiz as alterações que vc sugeriu, mas como fica o método deleta agora?

public void deleta(List<String> fotos) {
		for(Iterator<String> it = fotos.iterator(); it.hasNext();) {
			File destino = new File(pastaImagens, it.hasNext());
			try {
				 destino.delete();
			} catch (Exception e) {
				throw new RuntimeException("Erro ao deletar imagem", e);
			}
		}
	
	}

É por ai? Tá acusando a mesma mensagem no “Iterator”.

G

Use da forma como eu fiz da primeira vez, já que agora você tem uma lista de Strings contendo o caminho de cada imagem.

public void deleta(List<String> fotos) { for(String foto: fotos) { File destino = new File(pastaImagens, foto); try { destino.delete(); } catch (Exception e) { throw new RuntimeException("Erro ao deletar imagem", e); } } }

O mais importante não é funcionar em sí, mas você entender. Me pergunta caso você tiver alguma dúvida.

Guevara

Resolvido Garcia! :smiley:
Está deletando todas as fotos da pasta, eu não entendi o que este trecho do código faz:

for(String foto: fotos)

Poderia me explicar?

Lucas_Cavalcanti

isso itera por todos os elementos da lista fotos, colocando o elemento atual na variavel foto…

Guevara

Obrigado pelo esclarecimento Lucas! :slight_smile:

G

Complementando, isso é o famoso for-each do Java. O resultado na pratica é o mesmo que:

for(int i=0; i<fotos.size(); i++) { Foto foto = fotos.get(0); }

Ou o mesmo que

Iterator itFotos = fotos.iterator(); while(itFotos.hasNext()) { Foto foto = itFotos.next(); }

Guevara

Valeu Garcia!
Eu tava tentando com o Iterator, mas tava montado errado, agora com esse novo método que o Java permite fica menos código tb.
Anotei essas dicas pra usar futuramente.
Abraço!

Criado 4 de maio de 2010
Ultima resposta 7 de mai. de 2010
Respostas 26
Participantes 4