Exibir imagem dinâmica do database com p: graphicImage e StreamedContent

Eu estou tentando exibir bytes de imagem que é salvo no database como um StreamedContent no

seguinte maneira:

 

 private StreamedContent content; // getter and setter public StreamedContent getImageF() { if (student.getImage() != null) { InputStream is = new ByteArrayInputStream(student.getImage()); System.out.println("Byte :"+student.getImage()); content = new DefaultStreamedContent(is, "", student.getStuID()); System.out.println("ddd ------------------------------- " + content); return content; } return content; } 

Isso retorna uma imagem em branco. Como isso é causado e como posso resolvê-lo?

O stdout imprime o seguinte:

 INFO: Byte :[B@a2fb48 INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@b0887b INFO: Byte :[B@a2fb48 INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@1d06a92 INFO: Byte :[B@d52f0b INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@39a60 INFO: Byte :[B@d52f0b INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@8c3daa INFO: Byte :[B@124728a INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@1dbe05b INFO: Byte :[B@124728a INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@66a266 INFO: Byte :[B@a2fb48 INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@1293976 INFO: Byte :[B@a2fb48 INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@17b7399 INFO: Byte :[B@d52f0b INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@1e245a5 INFO: Byte :[B@d52f0b INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@4a7153 INFO: Byte :[B@124728a INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@1561bfd INFO: Byte :[B@124728a INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent@47a8c2 

   

O requer um método getter especial. Ele será chamado duas vezes por imagem gerada, cada uma em uma solicitação HTTP completamente diferente.

A primeira solicitação HTTP, que solicitou o resultado HTML de uma página JSF, chamará o getter pela primeira vez para gerar o elemento HTML com a URL exclusiva e gerada automaticamente no atributo src que contém informações sobre qual bean e getter devem ser invocados sempre que o web browser estiver prestes a solicitar a imagem. Observe que o getter neste momento não precisa retornar o conteúdo da imagem. Ele não seria usado de forma alguma, pois não é assim que o HTML funciona (as imagens não são “inlined” na saída HTML, mas são solicitadas separadamente).

Depois que o navegador recuperar o resultado HTML como resposta HTTP, ele analisará a origem HTML para apresentar o resultado visualmente ao usuário final. Depois que o navegador encontrar um elemento durante a análise do código-fonte HTML, ele enviará uma nova solicitação HTTP no URL, conforme especificado em seu atributo src , para baixar o conteúdo dessa imagem e incorporá-la na apresentação visual. Isso invocará o método getter pela segunda vez, o que, por sua vez, deve retornar o conteúdo real da imagem.

No seu caso particular, o PrimeFaces aparentemente era incapaz de identificar e invocar o getter para recuperar o conteúdo da imagem real, ou o getter não retornava o conteúdo esperado da imagem. O uso do nome da variável #{item} e o lote de chamadas no log sugere que você o estava usando em uma ou uma . O mais provável é que o bean de apoio tenha escopo de pedido e o modelo de dados não seja preservado adequadamente durante a solicitação da imagem, e o JSF não poderá chamar o getter durante a rodada de iteração correta. Um bean com escopo de visão também não funcionaria, pois o estado de exibição do JSF não está disponível quando o navegador realmente solicita a imagem.


Para resolver este problema, sua melhor aposta é rewrite o método getter como tal, para que possa ser invocado por pedido, onde você passa o identificador de imagem único como um vez de confiar em algumas propriedades do bean de apoio que pode ficar “fora de sincronia” durante solicitações HTTP subseqüentes. Faz todo o sentido usar um bean gerenciado com escopo de aplicativo separado para isso que não possui nenhum estado. Além disso, um InputStream pode ser lido apenas uma vez, não várias vezes.

Em outras palavras: nunca declare StreamedContent nem qualquer InputStream ou mesmo UploadedFile como uma propriedade de bean; apenas crie-o @ApplicationScoped novo no getter de um bean @ApplicationScoped sem @ApplicationScoped quando o webbrowser realmente solicitar o conteúdo da imagem .

Por exemplo

        

Onde o StudentImages backup StudentImages pode ter esta aparência:

 @Named // Or @ManagedBean @ApplicationScoped public class StudentImages { @EJB private StudentService service; public StreamedContent getImage() throws IOException { FacesContext context = FacesContext.getCurrentInstance(); if (context.getCurrentPhaseId() == PhaseId.RENDER_RESPONSE) { // So, we're rendering the HTML. Return a stub StreamedContent so that it will generate right URL. return new DefaultStreamedContent(); } else { // So, browser is requesting the image. Return a real StreamedContent with the image bytes. String studentId = context.getExternalContext().getRequestParameterMap().get("studentId"); Student student = studentService.find(Long.valueOf(studentId)); return new DefaultStreamedContent(new ByteArrayInputStream(student.getImage())); } } } 

Por favor note que este é um caso muito especial em que executar a lógica de negócios em um método getter é completamente legítimo, considerando como o trabalha sob as capas. Invocando lógica de negócios em getters é geralmente desaprovado, veja também Por que o JSF chama getters várias vezes . Não use este caso especial como desculpa para outros casos padrão (não especiais). Observe também que você não pode usar o recurso EL 2.2 de passar argumentos de método como #{studentImages.image(student.id)} porque esse argumento não será #{studentImages.image(student.id)} no URL da imagem. Assim, você realmente precisa passá-los como .


Se você usar o OmniFaces 2.0 ou mais recente , considere o uso de seu lugar, que pode ser usado de forma mais intuitiva, com um método getter com escopo de aplicativo diretamente delegando ao método de serviço e suportando argumentos do método EL 2.2.

Assim, então:

      

Com

 @Named // Or @ManagedBean @ApplicationScoped public class StudentImages { @EJB private StudentService service; public byte[] getImage(Long studentId) { return studentService.find(studentId).getImage(); } } 

Veja também o blog sobre o assunto.

Tente include um tipo de mímica. No seu exemplo publicado, você tem como “”. A imagem em branco pode ser porque ela não reconhece o stream como um arquivo de imagem desde que você transformou esse campo em uma string vazia. Então adicione um tipo mime de image / png ou image / jpg e veja se isso funciona:

 String mimeType = "image/jpg"; StreamedContent file = new DefaultStreamedContent(bytes, mimeType, filename); 

Há algumas possibilidades aqui (e por favor poste a turma toda se não for isso).

1) Você não está inicializando a imagem corretamente 2) Seu stream está vazio, então você não está recebendo nada

Estou assumindo que student.getImage () tem uma assinatura de byte [], então primeiro certifique-se de que os dados estejam realmente intactos e representem uma imagem. Em segundo lugar – você não está especificando um tipo de conteúdo que deveria ser “image / jpg” ou qualquer coisa que você esteja usando.

Aqui está um código clichê para checar, estou usando o Primefaces 2 para isso.

 /** 'test' package with 'test/test.png' on the path */ @RequestScoped @ManagedBean(name="imageBean") public class ImageBean { private DefaultStreamedContent content; public StreamedContent getContent() { if(content == null) { /* use your database call here */ BufferedInputStream in = new BufferedInputStream(ImageBean.class.getClassLoader().getResourceAsStream("test/test.png")); ByteArrayOutputStream out = new ByteArrayOutputStream(); int val = -1; /* this is a simple test method to double check values from the stream */ try { while((val = in.read()) != -1) out.write(val); } catch(IOException e) { e.printStackTrace(); } byte[] bytes = out.toByteArray(); System.out.println("Bytes -> " + bytes.length); content = new DefaultStreamedContent(new ByteArrayInputStream(bytes), "image/png", "test.png"); } return content; } } 

e alguma marcação …

        

Se esse código funcionar, você está configurado corretamente. Apesar do fato de que é um código de lixo para os streams (não usá-lo na produção), ele deve lhe dar um ponto para solucionar problemas. Meu palpite é que você pode ter algo acontecendo em sua JPA ou outra estrutura de database em que o byte [] está vazio ou está formatado incorretamente. Alternativamente, você poderia ter apenas um problema de tipo de conteúdo.

Por fim, eu clonaria os dados do bean para que student.getImage () fosse copiado em uma nova matriz e fosse usado. Desta forma, se você tem algo desconhecido acontecendo (algo mais movendo o object ou alterando o byte [] você não está mexendo com seus streams.

Faça algo como:

 byte[] data = new byte[student.getImage().length] for(int i = 0; i < data.length; i++) data[i] = student.getImage()[i]; 

para que o seu bean tenha uma cópia (ou Arrays.copy () - o que flutua no seu barco). Eu não posso enfatizar o suficiente como algo simples como este / tipo de conteúdo é geralmente o que está errado. Boa sorte com isso.

A resposta do BalusC é (como de costume) a correta.

Mas mantenha uma coisa (como já foi afirmado por ele) em mente. A solicitação final é feita no navegador para obter o URL da tag construída. Isso não é feito em um ‘contexto jsf’.

Então, se você tentar, por exemplo, acessar o phaseId (log ou qualquer motivo)

 context.getCurrentPhaseId().getName() 

Isso resultará em um NullPointerException e a mensagem de erro enganosa que você obterá é:

 org.primefaces.application.resource.StreamedContentHandler () - Error in streaming dynamic resource. Error reading 'image' on type abSomeBean 

Demorei algum tempo para descobrir qual era o problema.