如何return一个iText PDF文档到客户端
How to return an iText PDF document to the client side
我正在尝试 return 从服务器端到客户端的 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;
}
这是return发送到服务器端的:
byte[] pdfBytes = ScoutTranscript.main(listymAwards, scoutName, groupName);
System.out.println("Point 3");
return pdfBytes;
然后 returned 到客户端:
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:
让我们从一个不以任何方式涉及 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 年前所做的那样) , 所以我审查了自己 ;-)
错误#2:
您无法通过以下方式替换您的代码来解决您的问题:
Window.open("data:application/pdf;base64,"+ new String(result),"_parent", "location=no");
你不能那样做,因为你犯了第二个错误:result
中的字节代表一个 二进制文件 ,而 Java 脚本在您的浏览器中需要一个 Base64 编码的文件。 Base64 编码用于将二进制转换为文本,反之亦然。参见 What is base 64 encoding used for?
如果要将二进制 PDF 文件作为 Base64 编码的字符串发送到浏览器,则必须对字节进行 Base64 编码。这可以用这个class来完成: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。你可能想详细说明一下。在我看来,这是个坏主意。
应该怎么做:
通常,您将编写一个在应用程序服务器中运行的 Servlet
。可以使用 URL 从浏览器访问该 servlet。 您不需要按照其他答案中的建议将生成的文件保存在服务器上(我否决了该答案,因为它没有帮助而且完全错误)。您创建 ByteArrayOutputStream
然后获取 byte[]
的方法是正确的,但是您必须将这些字节提供给 HttpServletResponse
对象。
有关完整示例,请参阅 。
关于Window.open()
您可以在客户端使用Window.open()
,以新的window打开网页。例如:
window.open("http://www.itextpdf.com");
您可以提供包含此代码段的页面,但在您的情况下,您必须将 http://www.itextpdf.com
替换为为您的 servlet 定义的 URL。
您可能已经在此处找到了您的 "solution":Opening PDF String in new window with javascript
但是如果你阅读评论,你会注意到这种方法在与某些浏览器结合使用时存在问题。
我正在尝试 return 从服务器端到客户端的 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;
}
这是return发送到服务器端的:
byte[] pdfBytes = ScoutTranscript.main(listymAwards, scoutName, groupName);
System.out.println("Point 3");
return pdfBytes;
然后 returned 到客户端:
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:
让我们从一个不以任何方式涉及 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 年前所做的那样) , 所以我审查了自己 ;-)
错误#2:
您无法通过以下方式替换您的代码来解决您的问题:
Window.open("data:application/pdf;base64,"+ new String(result),"_parent", "location=no");
你不能那样做,因为你犯了第二个错误:result
中的字节代表一个 二进制文件 ,而 Java 脚本在您的浏览器中需要一个 Base64 编码的文件。 Base64 编码用于将二进制转换为文本,反之亦然。参见 What is base 64 encoding used for?
如果要将二进制 PDF 文件作为 Base64 编码的字符串发送到浏览器,则必须对字节进行 Base64 编码。这可以用这个class来完成: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。你可能想详细说明一下。在我看来,这是个坏主意。
应该怎么做:
通常,您将编写一个在应用程序服务器中运行的 Servlet
。可以使用 URL 从浏览器访问该 servlet。 您不需要按照其他答案中的建议将生成的文件保存在服务器上(我否决了该答案,因为它没有帮助而且完全错误)。您创建 ByteArrayOutputStream
然后获取 byte[]
的方法是正确的,但是您必须将这些字节提供给 HttpServletResponse
对象。
有关完整示例,请参阅
关于Window.open()
您可以在客户端使用Window.open()
,以新的window打开网页。例如:
window.open("http://www.itextpdf.com");
您可以提供包含此代码段的页面,但在您的情况下,您必须将 http://www.itextpdf.com
替换为为您的 servlet 定义的 URL。
您可能已经在此处找到了您的 "solution":Opening PDF String in new window with javascript
但是如果你阅读评论,你会注意到这种方法在与某些浏览器结合使用时存在问题。