在工做中,有使用Java生成大量的电子凭证,也就是PDF。在以前的文章中,有写过,如何经过Java生成PDF。这里就再也不描述,写这篇文章主要是为了记录我在使用Java生成PDF过程当中犯过的错,以及踩过的坑。html
以前在使用生成方案的时候,咱们一开始采用的是《PD4ML》框架,这个框架相对来讲,要小巧些。可是不足的是:它是闭源的。在批量生成PDF文件时会出现内存泄漏的现象。往往咱们在分析内存缘由时,都止步于下面这一段代码:java
private void createPdfByHtml(String html, File file) throws Exception{ FileOutputStream fos = new FileOutputStream(file); ... // 这一行 pd4ml.render(sr, fos, new URL("http://"), "UTF-8"); }
所以,咱们不得不考虑换一个第三方库。目前咱们使用的是开源库《iText》,到目前为止,稳定运行。linux
(咱们在选择第三方框架时,尽可能选择开源的,由于在出现问题后,咱们能够经过查看源代码分析,甚至能够修改源代码进行优化)。程序员
这个问题是这样的,咱们生成PDF时为了支持中文字体,须要经过路径加载指定的字体文件。而开发环境是windows,测试/生产环境均是Linux。字体放在面试
/resources/fonts/
由于:windows
在Window下获取Thread.currentThread()为null值微信
在Linux下获取this.class.getClass().getResource(“/“).getPath();获取为null值app
因此在加载路径时,咱们经过下述方法进行了兼容处理。框架
/** * 应用场景: * 1.在windows下,使用Thread.currentThread()获取路径时,出现空对象,致使不能使用 * 2.在linux下,使用PdfUtils.class获取路径为null, * 获取字体路径 * @return */ private static String getFontPath(){ String path=""; // 1. 生产环境路径 ClassLoader classLoader= Thread.currentThread().getContextClassLoader(); URL url = (classLoader==null)?null:classLoader.getResource("/"); String threadCurrentPath = (url==null)?"":url.getPath(); // 2. 若是线程获取为null,则使用当前PdfUtils.class加载路径 if(StringUtil.isBlank(threadCurrentPath)){ path = PdfUtils.class.getClass().getResource("/").getPath(); } // 3.拼接字体路径 StringBuffer stringBuffer = new StringBuffer(path); stringBuffer.append("/fonts/SIMKAI.TTF"); path = stringBuffer.toString(); logger.info("getFontPath threadCurrentPath: {} path: {}",threadCurrentPath,path); return path; }
从这个方面也能体现一个程序员常见现象。测试
为何在我电脑上是好的,在你电脑上不行,必定是你电脑问题。
不该该啊,在我电脑上是好的。绝对没问题。
小伙伴们知道这个梗吗? 这也是不少程序员使用与生产环境一致的系统做为开发机的缘由。
问题是这样的。是因为在生成PDF文件时转换字节流时没有指定编码致使的: 以下所示:
public static void createdPdfByItextHtml(String htmlContent,File file){ ... try { inputStream= new ByteArrayInputStream(htmlContent.getBytes()); outputStream = new FileOutputStream(file); ...
此处省略掉部分代码,此时htmlContent.getBytes()
该方法没有指定默认的编码致使的。具体的缘由,在《初探JDK源码之默认字符集》这篇文章中有详细的描述。
这个坑是这样的,由于正式公文的字体是:Kaiti_GB2312
的字体。咱们使用过一段时间发现,Kaiti_GB2312
字体对于生僻字支持不友好。
例如:
例如: 陈垚,经过Kaiti_GB2312生成后就只能显示为: 陈
其中垚
字就没有正常显示。这个问题是很是严重的。若是电子凭证中姓名错误,也就表明,这份电子凭证是没有法律效应的。在通过测试后,咱们最终使用了Kaiti
字体,代替了Kaiti_GB2312
。(这个坑,还真的不容易发现)
5. 枚举传递类型
若是Dubbo接口中使用枚举做为参数时,若是只有单方面更新枚举的值。会致使序列化出错的问题。也就是说:
调用方在更新枚举后,若是接受方不一样时更新枚举,会致使接收方的参数为空对象。
注意点:
Dubbo服务调用之间,尽可能不要使用enum类型。
使用过itext
的朋友应该知道。HTML内容是须要严格的标签的。也就是说,一个开始标签,对应一个结束标签。若是不匹配,则会生成失败。(像<img src="www.baidu.com"/>
) 标签除外。
错误的html:
<html> <head> <title></title> </head> <body> <span>Hello World!</span></span> </body> </html>
上面这段html内容,就多了一个</span>
结束标签,就属于非严格的HTM语义标签。
执行后,就会有下面这类错误:
com.itextpdf.tool.xml.exceptions.RuntimeWorkerException: Invalid nested tag span found, expected closing tag body. at com.itextpdf.tool.xml.XMLWorker.endElement(XMLWorker.java:134) at com.itextpdf.tool.xml.parser.XMLParser.endElement(XMLParser.java:396)
有些系统,看起来容易。作起来可并不是易事。在实现期间可能会遇到各类各样的奇葩问题。但偏偏,就是这些怪问题。给咱们积累了经验。我一直以为,在写代码方面,是一个“倒霉”的人,由于各类奇怪的问题,都能让我遇到。
公众号内回复:【PDF】,便可获取项目。记得更换字体哦!
相关阅读:
扫码关注,一块儿进步
我的博客: http://www.andyqian.com