最近几天,项目要求一个小需求,用PDF格式导出聊天记录。虽然之前没有自己实现过,但是觉得网上这种例子应该很多,于是找了找。例子很多,但是这个坑也是挺多的。主要是使用iText 和flying saucer实现,xmlworker不知道怎么的,我本地一直导出的PDF没有内容,就暂时没有使用。
下面说下我自己的实现过程,以此记录下吧:
1.需求描述:
选择客服,可以该客服负责的多个客户的聊天记录,保留原有聊天记录样式
2.思路:
1)保留原有样式,如果自己向PDF里写的话,很不容易实现,所以想到应该用H5。
2)用JSP动态生成H5页面
3)多个客户要生成多个PDF文件,如果要下载的话,应该打包下载
4)循环每个客户的H5页面,生成PDF,先临时存放到服务器上,再Zip打包下载后删除
需求很简单,思路也很简单的哈。。但是这样做,其实问题也挺多的。
3.问题:
1)每个H5都要解析,如果页面很大的话或者H5比较多的话,运行时间会比较长
2)PDF文件本身很大,先生成在服务器上,再打包下载,也会增加时间
解决思路《暂时在项目组还未实现》
1)多个文件,启用多个线程去分别运行
2)直接用流的形式,进行zip打包
下面粘贴一些自己实现的主要代码:
这是生成PDF的方法。这里主要遇到的问题是解决中文字体不显示的问题。因为我本地是win电脑,可以使用win自带的字体,本地测试没问题,但是放到服务器上就傻眼了,不显示中文了。然后看到网上说可以使用iTextAsian.jar这个第三方字库jar包,就高兴的去使用了。如果说自己向PDF里写内容的话,这个jar包是没问题的。但是我使用了ITextTenderer去读取H5这个方法,看源码正好和那个方法冲突。最后没辙,只能把简体中文字体包,打包到项目中。。。。。
*需要注意的是,如果读取H5要使用中文,必须在页面中设置字体。还有一些问题,是页面中的链接地址,必须是绝对地址或者是程序中给出。如果不是的话,程序会尝试多次去寻找链接,很费时间。。最后也是最重要一点,解析的HTML必须为XHTML,就是格式必须规范。如果不规范,就会解析异常的
public void createPDF(String destPath,String htmlAddress) throws IOException, DocumentException{
<span style="white-space:pre"> </span>//destPath 服务器临时目录地址
OutputStream os = new FileOutputStream(destPath);
<span style="white-space:pre"> </span>
ITextRenderer renderer = new ITextRenderer();
renderer.setDocument(htmlAddress);
ITextFontResolver fontResolver = renderer.getFontResolver();
<span style="white-space:pre"> </span> //解决中文乱码问题
//1.使用window自带中文字体
/*fontResolver.addFont("C:/WINDOWS/Fonts/SIMSUN.TTC", BaseFont.IDENTITY_H,
BaseFont.NOT_EMBEDDED); */
<span style="white-space:pre"> </span>//找到项目中的简体中文字体包
URL url = this.getClass().getResource("/");
fontResolver.addFont(url.toString(), BaseFont.IDENTITY_H,
BaseFont.NOT_EMBEDDED);
renderer.layout();
renderer.createPDF(os);
}
后面就是一些打包压缩输出的代码了
/**
* 打包zip压缩文件
* @param files
* @param baseName
* @param zos
* @throws IOException
*/
private void zipFile(List<File> files,String baseName,ZipOutputStream zos) throws IOException{
for (File file : files) {
zos.putNextEntry(new ZipEntry(baseName+file.getName()));
FileInputStream fis = new FileInputStream(file);
byte[] buf = new byte[1024];
int len = 0;
while((len=fis.read(buf))!=-1){
zos.write(buf, 0, len);
}
fis.close();
}
}
设置返回头,用流的方式返回给浏览器
<span style="white-space:pre"> </span>response.setContentType("APPLICATION/OCTET-STREAM");
response.setHeader("Content-Disposition", "attachment;filename="+getZipFileName());
ZipOutputStream zos;
try {
zos = new ZipOutputStream(response.getOutputStream());
zipFile(files, "", zos);
zos.close();
//删除生成的PDF
for (File file : files) {
file.delete();
}
} catch (IOException e) {
log.error("导出聊天记录PDF异常",e);
}
代码真的很少,但是要想按照自己的需求去实现,坑还是挺多的。如果你生成的页面太大了的话,这个工具是不会自动帮你折行的,所以也需要给页面设置个大小。
在页面中加上这个,表示页面是A4的大小。
好啦,给上个结果图看看。也欣慰下哈