首页 文章

如何将iText PDF文档返回给客户端

提问于
浏览
0

我试图将iText生成的PDF从服务器端返回到客户端,以使用户能够存储它 . 我关注How to convert iTextPDF Document to Byte Array(AceFunk)

private static ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

  public static byte[] main(java.util.List<Transcript> listymAwards, String scoutName, String groupName) {
  Document document = new Document(PageSize.A4, 0f, 0f, 0f, 0f);

try {

  //PdfWriter.getInstance(document, new FileOutputStream(FILE));
  PdfWriter.getInstance(document, byteArrayOutputStream);  // Do this BEFORE document.open()

  document.open();
  addMetaData(document);
  addImages(document);
  addTitlePage(document, scoutName);

  //Add the table of achievements
  if (listymAwards == null || listymAwards.isEmpty()) {
      //Nothing to do.
      //System.out.println("Scout not found.");
  }else{

      Paragraph preface = new Paragraph();

      PdfPTable table = new PdfPTable(3);
      table.setWidths(new int[]{1, 3, 1});
      table.setHeaderRows(1);

      PdfPCell c1 = new PdfPCell(new Phrase("Section"));
      c1.setHorizontalAlignment(Element.ALIGN_CENTER);
      table.addCell(c1);

      c1 = new PdfPCell(new Phrase("Award"));
      c1.setHorizontalAlignment(Element.ALIGN_CENTER);
      table.addCell(c1);

      c1 = new PdfPCell(new Phrase("Date"));
      c1.setHorizontalAlignment(Element.ALIGN_CENTER);
      table.addCell(c1);
      table.setHeaderRows(1);

      String storedName = null;
      int noRows = 0;
      String firstTable = "Yes";
      DateFormat df1 = new SimpleDateFormat("yyyy-MM-dd");
      DateFormat df2 = new SimpleDateFormat("dd-MM-yyyy");
      // We add three empty lines
      addEmptyLine(preface, 1);
      addEmptyLine(preface, 1);
      addEmptyLine(preface, 1);

      for (final Transcript scoutNamesDescription : listymAwards) {
          if (firstTable.equals("Yes") && noRows > 30){ // Change this to number of rows required
              noRows = 0;
              firstTable = "No";
              document.add(table);
              document.newPage();
              table.flushContent();
          }else{
              if (firstTable.equals("No") && noRows > 50){ // Change this to number of rows required
                  // We add three empty lines if not the first table
                  document.add(preface);
              }
          }

          noRows++;

          if (scoutNamesDescription.getSection().equals(storedName)){
              table.addCell(" ");
          }else{
              storedName = scoutNamesDescription.getSection();
              table.addCell(scoutNamesDescription.getSection());
          }
          table.addCell(scoutNamesDescription.getAwardName());

          Date awardedDate = df1.parse(scoutNamesDescription.getAwardedDate());
          String awardedString = df2.format(awardedDate);
          table.addCell(awardedString);
      }
      //Print the remaining rows.
      // We add three empty lines if not the first table
      if (firstTable.equals("No")){
          document.add(preface);
      }else{
          firstTable = "No";
      }
      document.add(table);
  }

  //Add signature
  addSignaturePage(document, groupName);

  document.close();


} catch (Exception e) {
  e.printStackTrace();
}

byte[] pdfBytes = byteArrayOutputStream.toByteArray();
return pdfBytes;

}

这将返回到服务器端:

byte[] pdfBytes = ScoutTranscript.main(listymAwards, scoutName, groupName);
    System.out.println("Point 3");

    return pdfBytes;

然后返回到客户端:

Window.open("data:application/pdf;base64,"+result,"_parent", "location=no");

我收到错误消息的地方:

This site can’t be reached

The webpage at data:application/pdf;base64,[B@154 might be temporarily down or it may have moved permanently to a new web address.

1 回答

  • 2

    Error #1:

    让我们从一个不涉及iText的小测试开始 . 试试这个:

    byte[] test = "Test".getBytes();
    System.out.println("Test " + test);
    

    什么写入输出?就我而言,它是:

    Test [B@3da3da69
    

    [ 表示我正在尝试将数组转换为 String ; B 表示该数组包含字节; @ 将类型与ID分开;后面的字符是十六进制格式的ID(哈希码) . 见[Java: Syntax and meaning behind "B@1ef9157"? Binary/Address?

    如果 result 的类型为 byte[] 且您有此行:

    Window.open("data:application/pdf;base64,"+result,"_parent", "location=no");
    

    然后 "data:application/pdf;base64,"+result 会产生类似 "data:application/pdf;base64,[B@154" 的内容 . 这没有任何意义,是吗?

    现在试试这个:

    byte[] test = "Test".getBytes();
    System.out.println("Test " + new String(test));
    

    输出是:

    Test Test
    

    你正在使用 byte[] ,好像它是 String . 这是你的第一个错误 .

    我会说一些令人讨厌的东西,因为这不是Java开发人员会犯的错误 . 但是,我刚看了你的生物,我看到你是Java的新手,你(可能)教你自己如何使用Java编写代码(就像我20年前那样),所以我自己进行了审查;-)

    Error #2:

    您无法通过以下方式替换代码来解决问题:

    Window.open("data:application/pdf;base64,"+ new String(result),"_parent", "location=no");
    

    您不能这样做,因为您正在发出第二个错误: result 中的字节表示二进制文件,浏览器中的JavaScript需要Base64编码文件 . Base64编码用于将二进制转换为文本,反之亦然 . 见What is base 64 encoding used for?

    如果要将二进制PDF文件作为Base64编码的字符串发送到浏览器,则必须对字节进行Base64编码 . 这可以通过这个类来完成:http://itextsupport.com/apidocs/itext5/latest/com/itextpdf/text/pdf/codec/Base64.html

    例如:

    Window.open("data:application/pdf;base64,"+ Base64.encodeBytes(result),"_parent", "location=no");
    

    这应该已经适用于某些浏览器,但并非适用于所有浏览器 . 我不知道你在哪里使用 Window.open() ,也不知道你为什么要参与Base64 . 您可能想要详细说明 . 在我看来,这是一个坏主意 .

    How it should be done:

    通常,您将编写在应用程序服务器中运行的 Servlet . 可以使用URL从浏览器访问该servlet . You do not need to save the generated file on the server 正如另一个答案所建议的那样(我对这个答案进行了低估,因为它没有用,而且完全错了) . 创建 ByteArrayOutputStream 然后获取 byte[] 的方法是正确的,但您必须将这些字节提供给 HttpServletResponse 对象 .

    有关完整示例,请参阅How can I serve a PDF to a browser without storing a file on the server side? .

    Regarding Window.open()

    您可以在客户端使用 Window.open() ,在新窗口中打开网页 . 例如:

    window.open("http://www.itextpdf.com");
    

    您可以提供包含此代码段的页面,但在您的情况下,您必须将 http://www.itextpdf.com 替换为为您的servlet定义的URL .

    您可能在这里找到了"solution":Opening PDF String in new window with javascript

    但是如果您阅读了这些评论,您会发现这种方法与某些浏览器相结合会产生问题 .

相关问题