Social Icons


Pages

quinta-feira, 23 de agosto de 2012

Como exibir um PDF numa página JSF quando o arquivo está fora do container web

Confesso: tentei encontrar um título menor pra descrever esta situação, mas não encontrei... Resumindo, o problema é este: se uma página JSF tivesse que exibir um documento PDF que já estivesse dentro do WebContent, era moleza: bastava criar um link apontando pra ele e ponto final. Mas, e se o arquivo estiver, por exemplo, em um servidor da rede cujo o usuário final não tenha acesso?

Uma solução é: importar o conteúdo deste arquivo PDF em um array de bytes e, com isto, enviá-lo para o Response do JSF.

Vamos simular esta solução: criaremos uma aplicação simples, onde o usuário indica em qual caminho está o arquivo PDF, importa e visualiza o PDF no navegador. Ficará assim:

Para isto, basta um ManagedBean e uma página JSF. Sem mais delongas, eis o código, devidamente comentado:

index.xhtml:
<html
 xmlns="http://www.w3.org/1999/xhtml"
 xmlns:f="http://java.sun.com/jsf/core"
 xmlns:h="http://java.sun.com/jsf/html">
<h:head />

<h:body>
 <h:form>
  <h:panelGrid columns="2">
   <h:outputText value="Arquivo:" />
   <h:inputText value="#{control.arquivo}" />
   <h:commandButton
    value="Importar Arquivo"
    action="#{control.importarArquivo}" />
   <h:commandLink
    value="Visualizar PDF"
    action="#{control.visualizarPdf}"
    target="_blank" />
  </h:panelGrid>
 </h:form>
</h:body>
</html>

Control.java:
@ManagedBean
@SessionScoped
public class Control implements Serializable {

   // Array de bytes que armazenará o conteúdo do arquivo PDF
   private byte[] conteudoPdf;

   // Caminho completo do arquivo informado pelo usuário.
   // Ex: C:\Meus Documentos\boletim.pdf
   private String arquivo;

   public String getArquivo() {
      return arquivo;
   }

   public void setArquivo(String arquivo) {
      this.arquivo = arquivo;
   }

   public void importarArquivo() {
      try {
         // Cria um objeto File a partir do caminho especificado
         File file = new File(arquivo);

         // Inicializa o array bytes com o tamanho do arquivo especificado.
         // Note a conversao para int, restringindo a capacidade maxima do
         // arquivo em 2 GB
         conteudoPdf = new byte[(int) file.length()];

         // Cria um InputStream a partir do objeto File
         InputStream is = new FileInputStream(file);

         // Aqui o InputStream faz a leitura do arquivo, transformando em um
         // array de bytes
         is.read(conteudoPdf);

         // Fecha o InputStream, liberando seus recursos
         is.close();
      } catch (Exception e) {
         e.printStackTrace();
      }
   }

   public void visualizarPdf() {

      FacesContext fc = FacesContext.getCurrentInstance();

      // Obtem o HttpServletResponse, objeto responsável pela resposta do
      // servidor ao browser
      HttpServletResponse response = (HttpServletResponse) fc
            .getExternalContext().getResponse();

      // Limpa o buffer do response
      response.reset();

      // Seta o tipo de conteudo no cabecalho da resposta. No caso, indica que o
      // conteudo sera um documento pdf.
      response.setContentType("application/pdf");

      // Seta o tamanho do conteudo no cabecalho da resposta. No caso, o tamanho
      // em bytes do pdf
      response.setContentLength(conteudoPdf.length);

      // Seta o nome do arquivo e a disposição: "inline" abre no próprio
      // navegador.
      // Mude para "attachment" para indicar que deve ser feito um download
      response.setHeader("Content-disposition", "inline; filename=arquivo.pdf");
      try {

         // Envia o conteudo do arquivo PDF para o response
         response.getOutputStream().write(conteudoPdf);

         // Descarrega o conteudo do stream, forçando a escrita de qualquer byte
         // ainda em buffer
         response.getOutputStream().flush();

         // Fecha o stream, liberando seus recursos
         response.getOutputStream().close();

         // Sinaliza ao JSF que a resposta HTTP para este pedido já foi gerada
         fc.responseComplete();
      } catch (IOException e) {
         e.printStackTrace();
      }
   }
}

Leia Também:

12 comentários:

  1. Boa Noite Maycon ,

    observei teu código e testei aqui e funcionou que é uma beleza. Gostaria muito de pedir sua ajuda em uma coisa.
    O meu problema é o seguinte:
    Tenho um botão em uma pagina que abre o Dialog e dentro deste eu quero abrir um pdf que esta em uma pasta no cliente e não consigo fazer ele ler e mostrar neste dialog ou mesmo salvar o documento na raiz do projeto para eu ler , mas não estou conseguindo nem abrir o pdf no dialog e nem mesmo gravar o pdf na raiz do projeto para abrir, por favor pode me ajudar???

    ResponderExcluir
  2. Olá Paulo,
    A partir de um servidor JSF, não é possível acessar diretamente um arquivo na máquina do cliente. Você precisará de um algum mecanismo para intermediar este processo, como por exemplo, um file upload... dê uma olhada neste post: https://community.oracle.com/thread/2152059?tstart=0

    Agora, para exibir um PDF num dialog, dá pra fazer sim. Você está utilizando RichFaces, Primefaces?

    ResponderExcluir
    Respostas
    1. Desde já obrigado pela resposta, vou te mostrar o codigo, quem sabe vc possa me ajudar:
      Quando eu gero o PDF é este bean que uso :
      [code]
      public class MontaManual {
      private String programa_id;
      private String programa_objetivo;
      public static final String RESULT = "/pdf/out.pdf";

      public String getPrograma_id() {
      return programa_id;
      }
      public void setPrograma_id(String programa_id) {
      this.programa_id = programa_id;
      }
      public String getPrograma_objetivo() {
      return programa_objetivo;
      }
      public void setPrograma_objetivo(String programa_objetivo) {
      this.programa_objetivo = programa_objetivo;
      }

      public void gerar_manual() throws IOException, DocumentException {

      Document document = new Document();
      PdfCopy copy = new PdfCopy(document, new FileOutputStream(RESULT));
      document.open();

      PdfReader reader1 = new PdfReader("C:/Temp/CapaDoManual.pdf");
      int n1 = reader1.getNumberOfPages();

      PdfReader reader2 = new PdfReader("C:/Temp/SumarioDoManual.pdf");
      int n2 = reader2.getNumberOfPages();

      PdfReader reader3 = new PdfReader("C:/Temp/CorpoDoManual.pdf");
      int n3 = reader3.getNumberOfPages();

      PdfImportedPage page;
      PdfCopy.PageStamp stamp;

      Font f = new Font(FontFamily.TIMES_ROMAN, 10, Font.NORMAL);
      for (int i = 0; i < n1; ) {
      page = copy.getImportedPage(reader1, ++i);
      stamp = copy.createPageStamp(page);
      // add page numbers
      PdfPTable foot = new PdfPTable(new float[] { 0.8f, 0.2f });
      PdfPCell cell3 = new PdfPCell(new Phrase("PegasusIV - TT - AF - "+programa_id+" - " + programa_objetivo,f));
      cell3.setHorizontalAlignment(Element.ALIGN_LEFT);
      cell3.setVerticalAlignment(Element.ALIGN_TOP);
      cell3.setBorder(1);
      foot.addCell(cell3);
      PdfPCell cell4 = new PdfPCell(new Phrase(String.format("Pagina %d / %d", i, n1 + n2 + n3),f));
      cell4.setHorizontalAlignment(Element.ALIGN_RIGHT);
      cell4.setVerticalAlignment(Element.ALIGN_TOP);
      cell4.setBorder(1);
      foot.addCell(cell4);
      foot.setTotalWidth(page.getWidth() - document.leftMargin() - document.rightMargin());
      foot.writeSelectedRows(0, -1, document.leftMargin(), document.bottomMargin(), stamp.getUnderContent());
      stamp.alterContents();
      copy.addPage(page);
      }
      for (int i = 0; i < n2; ) {
      ...
      }
      for (int i = 0; i < n3; ) {
      ...
      }
      document.close();
      reader1.close();
      reader2.close();
      reader3.close();

      }
      }
      [/code]

      Excluir
  3. Este comentário foi removido pelo autor.

    ResponderExcluir
  4. Este comentário foi removido pelo autor.

    ResponderExcluir
  5. Este comentário foi removido pelo autor.

    ResponderExcluir
  6. quando executo a gravação ele diz que não consegue, a mensagem é:
    "Caused by: javax.faces.el.EvaluationException: java.io.FileNotFoundException: \pdf\out.pdf (O sistema não pode encontrar o caminho especificado)"

    na minha pagina o codigo é este:

    p:commandButton value="Manual" title="gera o MANUAL TECNICO do programa especificado" action="#{controlePaginaEspecificacaoTipo4.emitirManual(programa)}" oncomplete="dialogManual.show()" ajax="true" icon="ui-icon-document">

    /p:commandButton>

    ...

    h:form id="formManual">

    p:panel id="panelManual">
    p:media width="100%" height="600" value="/pdf/out.pdf" type="application/pdf"/>
    /p:panel>
    /p:dialog>
    /h:form>}

    se eu salvo o arquivo usando RESULT = "c:\\temp" eu não consigo ler na pagina.
    Entende meu problema?
    eu estou usando PRIMEFACES 4.0 HIBERNATE
    Já procurei em muitos lugares mas não achei nada que eu possa usar.

    desde já obrigado.

    ResponderExcluir
    Respostas
    1. Olá Paulo! Analisei o seu código, e vi que já passei por alguns dos problemas que você está passando agora. Na verdade, eu tenho algumas sugestões que podem alterar um pouco a forma de fazer isto. Passa o seu e-mail pra mim pra gente trocar umas idéias.

      Excluir
  7. boa noite maycon! Primeiramente queria dizer que gostei muito desse post e achei muito util...porém estou com um pequeno problema....como vc faria para fazer o upload de um PDF nesse servidor usando o FileUpload do primefaces? Ja procurei em tudo e não achei um tutoorial decente que use um arquivo PDF como exemplo...so achei com imagens!

    Você poderia mostrar um exemplo de como salvar o PDF no servidor usando o FileUpload? Agradeço desde já e aguardo sua resposta!

    ResponderExcluir
  8. meu deus te amo, caralho procurei isso a internet inteira mas só teu código me ajudou. <3

    ResponderExcluir
  9. Meu código está exatamente igual ao seu, no entanto ele nunca exibe em tela, somente baixa o relatorio pdf, vc pode me ajudar?

    Segue meu código
    public void getRelatotio(List lista, Long id) {
    stream = this.getClass().getResourceAsStream("/jasper/memorando.jasper");
    Map params = new HashMap();
    baos = new ByteArrayOutputStream();
    params.put("idDocumento", id);
    try {
    JasperReport report = (JasperReport) JRLoader.loadObject(stream);
    JasperPrint print = JasperFillManager.fillReport(report, params, getConexao());
    JasperExportManager.exportReportToPdfStream(print, baos);
    response.reset();
    response.setContentType("aplication/pdf");
    response.setContentLength(baos.size());
    response.setHeader("Content-disposition", "inline; filename=relatorio.pdf"); //se quiser forçar o download do relatorio mudar de inline para attachment
    response.getOutputStream().write(baos.toByteArray());
    response.getOutputStream().flush();
    response.getOutputStream().close();
    context.responseComplete();
    fecharConexao();
    } catch (JRException ex) {
    new RuntimeException("Exceção no metodo GetRelatorio do Relatorio");
    } catch (IOException ex) {
    new RuntimeException("Exceção no metodo GetRelatorio do Relatorio");
    }
    }

    ResponderExcluir
  10. Pessoal, eu quero carregar o ficheiro que estejo no disco C:, por exemplo e mostrar no dialog via p:media.
    Serah que nao existe uma forma tao simples de o fazer???

    ResponderExcluir