Java 通过wkhtmltopdf在线生成PDF及遇到的坑

原创
2020/04/21 17:24
阅读数 1.1K

1. 下载安装wkhtmltopdf;

百度网盘:https://pan.baidu.com/s/1r6CpIlKfmatrrhM0C7sX4Q

提取码:u4da

2. 创建一个工具类WKHtmlToPdfUtilUtil.java,写入以下代码:

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.Date;

public class WKHtmlToPdfUtil {

	private static class ClearBufferThread implements Runnable {
		private InputStream in;
		
		public ClearBufferThread(InputStream inputStream) {
			this.in = inputStream;
		}
		
		@Override
		public void run() {
			BufferedReader br = null;
			
			try {
				br = new BufferedReader(new InputStreamReader(in));
				while ((br.readLine()) != null) {
					
				}
			} catch(IOException e) {
				e.printStackTrace();
			} finally {
				if (br != null) {
					try {
						br.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
				if (in != null) {
					try {
						in.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}
	
	/**
	 * html转PDF
	 * @param htmlFilePath html存放绝对路径
	 * @param fileName pdf文件名
	 * @param domName 文件夹名称
	 * @return
	 */
	public static String htmlToPdf(String htmlFilePath, String fileName, String domName) {
		Date date = new Date();
		//String dateDir = DateTimeUtil.getDateDir(date);
		String dateTimePath = DateTimeUtil.getDateTimePath(date);
		//int rand = (int)((Math.random()*9+1)*10000);
		// Long timeMillis = System.currentTimeMillis();
		// 以时间为单位,判断当前日期目录是否存在
		File file = new File("C:/test/" + domName);
		if (!file.exists()) {
			file.mkdir();
		}
		//String pdfFilePath = "C:/test/" + dateTimePath + rand + ".pdf";
		String pdfFilePath = "C:/test/" + domName + "/" + fileName + "(" + dateTimePath + ")" + ".pdf";
		Process process = null;
		try {
			process = Runtime.getRuntime().exec(getCommand(htmlFilePath, pdfFilePath));
			new Thread(new WKHtmlToPdfUtil.ClearBufferThread(process.getInputStream())).start();
			new Thread(new WKHtmlToPdfUtil.ClearBufferThread(process.getErrorStream())).start();
			process.waitFor();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (process != null) {
				process.destroy();
			}
		}
		return pdfFilePath;
	}
	
	/**
	 * string转html
	 * @param strHtml 字符串(html+css结合体)
	 * @return
	 */
	public static String strToHtml(String strHtml) {
		int rand = (int)((Math.random()*9+1)*10000);
		Long timeMillis = System.currentTimeMillis();
		String htmlFilePath =  "C:/test/pdf/" + timeMillis + rand + ".html";
		File file = new File("C:/zghky/pdf");
		if (!file.exists()) {
			file.mkdir();
		}
		
		String headHtml = "<!DOCTYPE html><html><head><meta charset=\"UTF-8\"/></head>"
				+ "<style>table, tr, td, th, tbody, thead, tfoot{"
				+ "page-break-inside: avoid !important;}</style><body>";
		String footHtml = "</body></html>";
		String contentHtml	= headHtml + strHtml + footHtml;
		
		OutputStream os = null;
		try {
			os = new FileOutputStream(htmlFilePath);
			os.write(contentHtml.getBytes("UTF-8"));
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (os != null) {
				try {
					os.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return htmlFilePath;
	}

	private static String getCommand(String htmlFilePath, String pdfFilePath) {
		String osName = System.getProperty("os.name");
		StringBuilder cmd = new StringBuilder();
		cmd.append("D:/wkhtmltopdf/bin/wkhtmltopdf.exe ");
		cmd.append(" ");
		// cmd.append(" --header-line");//页眉下面的线
		// cmd.append(" --header-center 这里是页眉这里是页眉这里是页眉这里是页眉 ");//页眉中间内容
		cmd.append(" --margin-top 1.8cm ");// 设置页面上边距 (default 10mm)
		cmd.append(" --margin-right 1.5cm ");// 设置页面右边距 (default 10mm)
		cmd.append(" --margin-bottom 1.8cm ");// 设置页面下边距 (default 10mm)
		cmd.append(" --margin-left 1.5cm ");// 设置页面左边距 (default 10mm)
		cmd.append(" --page-size Letter ");// 纸张大小A4, Letter, etc.
		// cmd.append(" --header-html file:///"+WebUtil.getServletContext().getRealPath("")+FileUtil.convertSystemFilePath("\\style\\pdf\\head.html"));//
		// (添加一个HTML页眉,后面是网址)
		// cmd.append(" --header-spacing 6 ");// (设置页眉和内容的距离,默认0)
		// cmd.append(" --footer-center (设置在中心位置的页脚内容)");//设置在中心位置的页脚内容
		// cmd.append(" --footer-html file:///"+WebUtil.getServletContext().getRealPath("")+FileUtil.convertSystemFilePath("\\style\\pdf\\foter.html"));//
		// (添加一个HTML页脚,后面是网址)
		// cmd.append(" --footer-line");//* 显示一条线在页脚内容上)
		// cmd.append(" --footer-spacing 6 ");// (设置页脚和内容的距离)
		cmd.append(" %s %s");
		// Windows
		if (osName.startsWith("Windows")) {// C:/Program Files/
		// return
		// String.format("C:/Program Files/wkhtmltopdf/bin/wkhtmltopdf.exe %s %s",
		// htmlFilePath, pdfFilePath);
		// return String.format("wkhtmltopdf/bin/wkhtmltopdf.exe %s %s",
		// htmlFilePath, pdfFilePath);
			return String.format(cmd.toString(), htmlFilePath, pdfFilePath);
		}
		// Linux
		else {
			return String.format("/opt/wkhtmltopdf/bin/wkhtmltopdf %s %s",
					htmlFilePath, pdfFilePath);
		}
	}
	
	public static void main(String[] args) {
		//String htmlFilePath = "D:/Temp/pdf/temp.html";
		//WKHtmlToPdfUtil.htmlToPdf(htmlFilePath);
	}
}

注意:(1)strToHtml()方法传进来的参数是指body部分;

(2)strToHtml()方法中字符串headHtml的值是样式;

(3)getCommand()方法中cmd.append("D:/wkhtmltopdf/bin/wkhtmltopdf.exe ");的路径是第一步安装的程序的路径;

(4)htmlToPdf()方法传进来的参数htmlFilePath是指strToHtml()方法返回来的生成的html路径。

遇到过的坑:

(1)通过wkhtmltopdf生成pdf时,有时会出现没有报错,但PDF没有合成成功,这是我们可以通过上述代码的getCommand()方法中打印出

String.format(cmd.toString(), htmlFilePath, pdfFilePath)这句获得HTML转PDF的命令语句然后到打开cmd.exe,定位到wkhtmltopdf安装的盘,然后输入获取到的命令(html要存在),这是如果出现以下报错:

Error: Failed loading page C:/test/...? (sometimes it will work just to ignore this error with --load-error-handling ignore) Exit with code 1 due to network error: ProtocolUnknownError

这是因为PDF文件名出现空格或特殊字符,而wkhtmltopdf又忽略这类问题导致文件创建失败的错误导致的,通过正则表达式去掉即可解决。

(2)防止HTML转PDF时页面由于分页而被分割,样式css写入

table, tr, td, th, tbody, thead, tfoot{page-break-inside: avoid !important}

(3)如果出现合并table单元格居中,文字被分割成两部分,上述解决页面分割方法也没有解决的话,请更新wkhtmltopdf的版本,版本0.12.5已经解决了这个问题(即我上面给的百度云链接就是这个版本)。

传送门:

Java ftp压缩文件打包成zip并通过浏览器下载功能下载文件及兼容ie问题解决:https://my.oschina.net/u/3986411/blog/3291220

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部