Método para deletar imagem da pasta [Resolvido]

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!!

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

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

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.

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!!

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

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?

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();

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. =)

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

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. =/

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

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. =/

[quote=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. =/
[/quote]

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); } } }

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.

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); } } }

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) 

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.

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.

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