JSF 2 – PRIMEFACES – PHOTOCAM COM IMAGECROPPER
O desenvolvimento de novas versões do Primefaces, como seu próprio líder fala, está em chamas. Na última semana timemos a conclusão e lançamento da versão 3 e no mesmo dia o lançamento de novos componentes da versão 3.1.
Neste POST irei demonstrar como utilizar o componente PHOTOCAM para capturar uma imagem da sua webcam e como recortar esta imagem em seguida utilizando o IMAGECROPPER.
No nosso exemplo utilizamos:
- Mojarra 2.1.3
- Primefaces 3.1 SNAPSHOT
- GlassFish 3.1.1
Let’s Rock!
Crie um novo projeto no Netbeans e dentro da pasta source crie um pacote para o managed bean, dentro da pasta web crie uma pasta para as imagens e nela uma outra pasta tmp. Veja a imagem abaixo com a estrutura básica do projeto:
Adicione o jar do primerfaces na versão indicada acima. Na pasta web crie o arquivo index.xhtml e insira nele o código abaixo:
Codificação 1
<!--?xml version='1.0' encoding='UTF-8' ?--> Exemplo PhotoCam + ImageCropper Aguarde...
No arquivo index.xhtml temos o componete imagecropper definido a seguir:
Codificação 2
No atributo value definimos um objeto do tipo org.primefaces.model.CroppedImage que receberá a imagem recortada com o componente, em initialCoords definimos onde o retângulo marcador do recorte deve aparecer quando a imagem for renderizada. Além disso, temos um id para o componete, a definição se ele está ou não renderizado na view e um outro atributo que merece um pouco mais de destaque.
No atributo image devemos especificar o caminho para a imagem capturada pela webcam, é salientar que neste atributo devemos colocar o caminho absoluto da imagem como por exemplo:
Para conseguir este caminho com precisão, coloque da forma como esta na codificação 2, pegando o caminho do contexto.
No componente photocam devemos definir um widgetVar para ser usado para chamar uma função javascript capture(), em update colocamos os ids do que deve ser atualizado ao capturar a imagem e em listenner o método a ser executado quando a captura for finalizada.
Codificação 3
Abaixo veremos a codificação do PhotoCamController:
Codificação 4
private CroppedImage imagemRecortada; private String foto; private String fotoRecortada; private String arquivoFoto; private String arquivoFotoRecortada; private boolean exibeImagemCapturada; private ServletContext servletContext; /*getters e setters*/ private String getNumeroRandomico() { int i = (int) (Math.random() * 10000); return String.valueOf(i); } private void criaArquivo(String arquivo, byte[] dados) { FileImageOutputStream imageOutput; try { imageOutput = new FileImageOutputStream(new File(arquivo)); imageOutput.write(dados, 0, dados.length); imageOutput.close(); } catch (FileNotFoundException ex) { Logger.getLogger(PhotoController.class.getName()).log(Level.SEVERE, null, ex); FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Caminho não encontrado!", "Erro")); } catch (IOException ex) { Logger.getLogger(PhotoController.class.getName()).log(Level.SEVERE, null, ex); FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Erro ao criar arquivo!", "Erro")); } } public void recortar() { verificaExistenciaArquivo(arquivoFotoRecortada); fotoRecortada = "fotoRecortada" + getNumeroRandomico() + ".png"; arquivoFotoRecortada = servletContext.getRealPath(File.separator + "imagens" + File.separator + "tmp" + File.separator + fotoRecortada); criaArquivo(arquivoFotoRecortada, imagemRecortada.getBytes()); FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, "Foto RECORTADA com sucesso!", "Informação")); } public void oncapture(CaptureEvent captureEvent) { verificaExistenciaArquivo(arquivoFoto); foto = "foto" + getNumeroRandomico() + ".png"; arquivoFoto = servletContext.getRealPath(File.separator + "imagens" + File.separator + "tmp" + File.separator + foto); criaArquivo(arquivoFoto, captureEvent.getData()); exibeImagemCapturada = true; FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, "Foto CAPTURADA com sucesso!", "Informação")); } private void verificaExistenciaArquivo(String arquivo) { if (arquivo != null) { File file = new File(arquivo); if (file.exists()) { file.delete(); } } } public PhotoController() { exibeImagemCapturada = false; servletContext = (ServletContext) FacesContext.getCurrentInstance().getExternalContext().getContext(); }
O método recortar() é chamado sempre que recortamos a imagem na view e o oncapture() é acionado pelo componente photocam sempre que a função javascript capture() for chamada. Veja que uso nomes randomicos para as imagens criadas, este é um truque para evitar cache do imagecropper, já que não há esta propriedade neste componente.
Muito simples!
Abaixo o resultado:
Benigno gostei muito do seu blog. Sou estudante de computação no IFRR, e estou desenvolvendo um sistema java web com primefaces, mas estou com dificuldade em trabalhar com photocam primefaces 3.0,
peço a voçê que envias um projeto com este tema, vou divulgar seu blog aquí na região norte do brasil.
Fábio, Boa Vista Roraima 14 de janeiro de 2012,
Agradeco…
Caro Fábio, posso enviar-lhe o projeto-base do post. Vou disponibilizar em um link para que todos possam realizar o download.
Oi Benigno, você disponibilizou os fontes?
[]s
Seu blog é excelente, muito didático!
Meus sinceros parabéns!
Tem como usar este recurso para digitalizar por exemplo um documento com o scanner?
Existe para o jsf algum componente para scanner?
Muy bueno tutorial, podrías mandarme EventoDAO.java, FotoDAO.java y ConnectionFactory.java.
Muchas gracias
Olá Benigno!
Parabéns pelo seu post! Seu blog está excelente!
Bem, mas indo direto ao assunto: Hoje fui usar o componente photocam do primefaces, e tive problemas com a função javascript capture(). Quando executo esta função por meio do evento onlick executando a chamada “photoCam.capture()” tenho o erro javascript:
webcam.capture is not a function
Pesquisando por este erro, vi que você teve este mesmo problema registrado no forum do primefaces e consta como resolvido.
Aqui verifiquei que este erro acontece no firefox (atualmente uso a versão 10, no linux). Abrindo a mesma página no Chromiun funciona perfeitamente.
Estranho que a demostração deste componente no site primefaces funciona perfeitamente.
Como resolveu este problema?
Obrigado pela atenção,
Israel
Grande Israle, tudo bem? Coloquei resolvido porque até hoje nunca resolveram um problema que eu tenha postado naquele forum :D, acho que dão prioridade ao forum empresarial(os que pagam). Simplesmente desisti.
Mas atualizando para a nova versão eu percebi que a frequencia do erro diminuiu.
De qualquer forma obrigado pela resposta!
Gostaria de aproveitar para passar uma dica: não é necessário passar o caminho completo para a imagem, o caminho relativo é o suficiente, ou seja, onde possui o código:
image=”#{pageContext.servletContext.contextPath}/imagens/tmp/#{photoController.foto}”
pode substituir tranquilamente para
image=”/imagens/tmp/#{photoController.foto}”
[]’s
Você testou usando template(facelets)? Ou alguma framework de reescrita de URL como PrettyFaces? Veja bem, eu recomendo usar da forma como ensino porque você pode usar em qualquer situação. Há um tempo atras eu havia testado os casos que falei e não funcionava, caso funcione agora, excelente.
Ops, só uma correção! Me enganei ao dizer que funciona no firefox pela demonstração do primefaces. Lá também não funciona.
Mais uma dúvida: Fiz um código semelhante a este, para testar o uso do photocam e imagecropper. No entanto, em um dado momento, o componente p:commandButtom para de invocar o método definido no action deste componente.
Percebo que isto acontece quando capturo a imagem da câmera e a renderizo num p:panel, via ajax (comportamento normal). No entanto, após esta captura, ao tentar executar o crop (action de um commandButton), simplesmente o método não é invocado (não ocorre a chamada de forma alguma ao método do managed bean, verifico depurando o código).
Percebi que antes de capturar a imagem (ou seja, antes de atualizar o panel com imagem capturada), ao clicar no botão que faz crop, o método é invocado.
Não tenho certeza, mas me parece que algum evento ajax acaba interferindo no comportamento do botão e o mesmo passa a não funcionar caso eu capture a imagem.
Não sei se a dúvida é devida a este post, mas se puder me ajudar e entender este comportamento.
Não estou trabalhando com arquivo de imagem diretamente, e sim com o seu fluxo de bytes codificado em base64, acabo não precisando gerar o arquivo. Não acredito ser este o problema, pois já tive situações semelhantes em outros casos sem inclusive estar manipulando arquivos.
Bem, se puder me ajudar, gostaria de trocar umas idéias contigo. Gostaria que visse meu código.
[]’s,
Israel
Como você deve saber o ciclo JSF tem várias fases e em uma delas, a de validação, ocorre antes da chamada da action. Muito provavelmente esteja ocorrendo algum erro de validação no formulário. Tente colocar um componente p:messages e no botão crop mande atualiza-lo. Caso aconteça algum erro na validação aparecerá no messages.
Obrigado pelas respostas! Não estava exibindo mensagens de erro, e como ainda tenho pouca experiência com jsf, acabei não ficando atento a este detalhe. Pela mensagem exibida no p:messages, vi que o erro era por trabalhar com o formato em base64, consequentemente deu erro de conversão da imagem capturada para o crop. Ainda não resolvi o problema, mas entendendo o motivo, já ficou fácil resolver.
Ok, mas veja bem… se estiver aparecendo um erro de conversão… tive um problema desses e era por conta do caminho da imagem, por isso indico que use da forma como ensinei. Veja esta postagem: http://stackoverflow.com/questions/5307882/primefaces-imagecropper-conversion-error-occur-croppedimage-is-null
Bem, realmente o problema foi o caminho, mas não diretamente. Para meu caso usei a tag imageCropper basicamente da seguinte forma:
Nada demais aí, só que a variável photoCamBean.imageEncoded é uma string codificada em Base64 e não um caminho. Então ela é algo como:
photoCamBean.imageEncoded = “data:image/jpeg;base64,/9j/4AAQSkZJRgABAQ…”
E juntando o que me disse a esta informação … No precesso de validação essa string é convertida em um CroppedImage que necessita de um caminho para a imagem original, e como este valor não é um caminho válido, gera o erro.
Não sei se é exatamente isto, então me corrija se estiver errado.
Vi que posso criar um Converter, mas, pelo que percebi, de qualquer forma terei que criar um arquivo em disco, já que o objeto CroppedImage necessita disto.
Então estarei usando como você mostrou aqui (especificando inclusive o caminho completo), é garantido e menos trabalhoso :). Pelo menos até amadurecer mais com os conceitos em jsf.
Ops … houve um corte no meu texto por usar as tags especiais aqui. Então esta é a forma que uso a tag imageCropper:
p:imageCropper id=”croppedImage” value=”#{photoCamBean.imageCroped}” image=”#{photoCamBean.imageEncoded}”
Onde encontro o link para dowload do exemplo?
parabens! muito bom!
Benigno,
Boa tarde!
Não está aparecendo a codificação 2 nem a 3.
Obrigada!
Gente, tem algum requisito para esse tutorial? Segui todos os passos, mas o meu está com vários erros. 😦
Benigno, tudo bem??? Bem, primeiramente obrigado, seu blog esta me ajudando bastante, gostaria de saber se tem algum projeto que fez com gráfico em barras (barChat), pq tento fazer usando sua estrutura de pizza, da muito pau… O que posso fazer?
nao tenho o projeto, mas posso te ajudar no que deu errado no seu. posta o codigo.
E outra pergunta, é possível alimentar o gráfico com informações resgatadas de um banco MySql?
com certeza
Consegui solucionar o problema do gráfico de barras, agora como posso alimentar o gráfico com informações do banco, ou seja, não estática?? E outra coisa,na minha aplicação ao clicar em um checkbox preciso mostrar um valor “x” em um outro campo inputext, como fazê-lo?? Olha o código abaixo…. Obrigado
Parabéns, voce teria os fontes em algum lugar para dowload?
Boa tarde Benigno! Teria como vc me enviar os fontes desse exemplo pra mim?
Olá, estou a uns dias procurando sobre como implementar este componente e só no seu blog que consegui algo consistente. Poderia me passar os fontes desse exemplo, Aqui não está aparecendo todo o código. Desde já obrigado!
Boa tarde, também já procurei muito na net a implementação destes componentes e não encontrei muita coisa, poderia me informar onde pego os fontes deste exemplo? Obrigado!
O triste é postar um excelente tutorial e não disponibilizar os fontes.
Benigno excelente post, todavia teria como vc disponibilizar os fontes, ou acertar as exibições dos códigos acima pois não está aparecendo corretamente! Desde já agradeço!
Benigno agradeço por ter ajudado bastante com seu post. já consegui resolver tudo porém gostaria de mudar o width do photocam sabe me informar como?????
JSF 2 – PRIMEFACES – PHOTOCAM COM IMAGECROPPER
teria como enviar por email os fontes desse projeto? Obrigado amigo
Os fontes estão no meu ultimo POST.
Só vim aqui agradecer a ajuda amigo.. peguei o seu exemplo e fiz uma adaptação para que a foto seja salvar num banco de dados em um campo do tipo Blob.. muito Obrigado pela ajuda.. Ajudou 100%..Valeu mesmo !!!
Can you share the source codes?
wow grate man 🙂 embedded two things