JAVA根据word模板生成合同,并能实现网页在线浏览/打印/下载

最近,

项目有这样一个需求:

 

      根据我选择的模板(docx文件),和我表单填的数据,生成相应的合同文件(docx),该合同要能网页在线浏览/打印/下载在合同中还要放置签字图片和身份证图片

我的实现:

1上传一份合同模板,在用户提交表单数据的时候,把数据写进合同模板,生成完整合同单独保存。这里需要用到docx模板插件能插入图片的那种(身份证读卡器和签字生成的图片要放进合同里)

2再把这份文件后缀是docx的合同文件转换成PDF,再存一份(因为要浏览,刚好多数浏览器支持网页浏览PDF文件和打印下载,不用我自己去实现,想着再转存一份pdf文件,就能实现需求了)。这里需要用到转PDF插件

3等于每个生成的合同都有两份,一份docx源文件,一份PDF格式副本。

4当我页面需要网页在线浏览/打印/下载的时候,就把PDF的副本放上去用另外一个PDF浏览打印插件去实现。这里需要用到PDF浏览打印的js(展示在网页上的是合同的PDF文件,可以浏览/打印/下载)

 

总共三个插件,本着开源(免费)的态度去找,最终淘汰了一些,我选择了以下三款插件

分别是:

1,poi-tl:docx模板引擎,作用是根据输入的符号把他替换成相应的值

 

JAVA根据word模板生成合同,并能实现网页在线浏览/打印/下载_第1张图片

 

模板:

 

 

生成后:

 

 

 

坑点:相关依赖jar多,少个包报错排查半天没查出来,还有就是它依赖poi的版本既不能太高,也不能低,不然报错。我用的是3.17

 

2,PDFObject:PDF浏览打印的js,只要导入这个js,把pdf文件所在的统一资源定位地址输进去就是,记住!不是绝对路径,是映射过的虚拟路径

 

JAVA根据word模板生成合同,并能实现网页在线浏览/打印/下载_第2张图片

 

3,liberOffice:基于office文件转换PDF文件的插件。

这个有点坑,容我细说:

模板生成工具类涉及的插件我查了后就定了两个(免费的)。分别是liberOffice和OpenOffice,他们各有优劣

liberOffice生成的pdf精准度高,配置相对简单,不需要敲指令,但是生成速度很慢,一个文件需要等待数秒以上,影响用户体验,有时会崩溃,感觉没OpenOffice稳定。

OpenOffice生成的pdf精准度低,可能失真和样式混乱。配置麻烦,每次重启服务器都要敲指令,但是生成速度很快很快,很少用时超过一秒的。

这是二者差别,因为插件都是用来生成合同用的,所以要高精准度,我选择了liberOffice,牺牲了速度(性能)。

下面附上我的工具类

 

根据LiberOffice生成pdf工具类,入参为文件全路径,执行完生成一份地址和名字一模一样的PDF文件,还需要配一下LiberOffice安装路径

package com.hk.Labor.utils;



import org.slf4j.Logger;
import org.slf4j.LoggerFactory;



import org.apache.commons.io.FileUtils;

import org.artofsolving.jodconverter.OfficeDocumentConverter;
import org.artofsolving.jodconverter.office.DefaultOfficeManagerConfiguration;
import org.artofsolving.jodconverter.office.OfficeManager;

 
import java.io.*;
 

import java.text.SimpleDateFormat;
import java.util.Date;

import java.util.regex.Pattern;

/**
 * 文件格式转换PDF 可转换的文件类型:各种office文件,图片以及普通txt
 */
public class DocConverter {
	protected static final Logger LOG = LoggerFactory.getLogger(DocConverter.class);
	private String fileString;
	private String outputPath = "";// 输入路径 ,如果不设置就输出在默认 的位�?
	private String fileName;
	private File pdfFile;
	
	private File docFile;

	public DocConverter(String fileString) {
		ini(fileString);
		System.out.println("文件路径"+fileString);
	}

	public DocConverter() {
		// TODO 自动生成的构造函数存根
	}

	/**
	 * * 重新设置file
	 * 
	 * @param fileString
	 *            32.
	 */
	public void setFile(String fileString) {
		ini(fileString);
	}

	/**
	 *  * 初始
	 * 
	 * @param fileString
	 *           
	 */
	private void ini(String fileString) {
		this.fileString = fileString;
		fileName = fileString.substring(0, fileString.lastIndexOf("."));
		docFile = new File(fileString);
		pdfFile = new File(fileName+ ".pdf");
	
	}
	
	/**
	 *  转PDF方法(LibleOffice插件)
	 * 
	 * @param file docFile, pdfFile
	 *       
	 */
	public  void doc2pdf() throws Exception {
	      String LibreOffice_HOME = getLibreOfficeHome();
	        String fileName = docFile.getName();
	        System.out.println(new SimpleDateFormat("yyyy-MM-dd HH-mm-ss").format(new Date()) + "文件" + docFile.getName());
	        System.out.println(fileName.substring(fileName.lastIndexOf(".")));
	        if (fileName.substring(fileName.lastIndexOf(".")).equalsIgnoreCase(".txt")) {
	            System.out.println("处理txt文件");
	            new DocConverter().TXTHandler(docFile);
	        }
	        DefaultOfficeManagerConfiguration configuration = new DefaultOfficeManagerConfiguration();
	        // libreOffice的安装目录
	        configuration.setOfficeHome(new File(LibreOffice_HOME));
	        // 端口号
	        configuration.setPortNumber(8100);
	        configuration.setTaskExecutionTimeout(1000 * 60 * 25L);
//	         设置任务执行超时为10分钟
	        configuration.setTaskQueueTimeout(1000 * 60 * 60 * 24L);
//	         设置任务队列超时为24小时
	        OfficeManager officeManager = configuration.buildOfficeManager();
	        officeManager.start();
	        System.out.println(new Date().toString() + "开始转换......");
	        OfficeDocumentConverter converter = new OfficeDocumentConverter(officeManager);
	        converter.getFormatRegistry();
	        try {
	            converter.convert(docFile, pdfFile);
	        } catch (Exception e) {
	            e.printStackTrace();
	            System.out.println("转换失败");
	        } finally {
	            officeManager.stop();
	        }
	 
	 
	        System.out.println(new Date().toString() + "转换结束....");

	}



	static String loadStream(InputStream in) throws IOException {
		int ptr = 0;
		in = new BufferedInputStream(in);
		StringBuffer buffer = new StringBuffer();
		while ((ptr = in.read()) != -1) {
			buffer.append((char) ptr);
		}
		return buffer.toString();
	}



	 /**
     * 打开libreOffice服务的方法
     *
     * @return
     */
    public String getLibreOfficeHome() {
        String osName = System.getProperty("os.name");
 
        if (Pattern.matches("Linux.*", osName)) {
            //获取linux系统下libreoffice主程序的位置
           //这里需要返回libreOffice安装路径,我的路径写在了自己建的Properties文件里
            return Utils.getProperties("libre_office_linux");
        } else if (Pattern.matches("Windows.*", osName)) {
            //获取windows系统下libreoffice主程序的位置
        	//这里需要返回libreOffice安装路径,我的路径写在了自己建的Properties文件里
            return Utils.getProperties("libre_office_windows");
        }
        return null;
    }

//测试方法
public static void main(String[] args) throws Exception {
	 String printfPDFPath = "E:/Labor/tplfile/党建交接.docx";
	  printfPDFPath =  printfPDFPath.replace("/", "\\\\");
		DocConverter demo = new  DocConverter(printfPDFPath);
		
	    demo.doc2pdf();
}

    /**
     * 转换txt文件编码的方法
     *
     * @param file
     * @return
     */
    public File TXTHandler(File file) {
        //或GBK
        String code = "gb2312";
        byte[] head = new byte[3];
        try {
            InputStream inputStream = new FileInputStream(file);
            inputStream.read(head);
            if (head[0] == -1 && head[1] == -2) {
                code = "UTF-16";
            } else if (head[0] == -2 && head[1] == -1) {
                code = "Unicode";
            } else if (head[0] == -17 && head[1] == -69 && head[2] == -65) {
                code = "UTF-8";
            }
            inputStream.close();
 
            System.out.println(code);
            if (code.equals("UTF-8")) {
                return file;
            }
            String str = FileUtils.readFileToString(file, code);
            FileUtils.writeStringToFile(file, str, "UTF-8");
            System.out.println("转码结束");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
 
        return file;
    }


	

}

根据OpenOffice生成pdf工具类,入参为文件全路径,执行完生成一份地址和名字一模一样的PDF文件,但每次开机都需要启动一条dos指令

package com.hk.Labor.utils;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.artofsolving.jodconverter.DocumentConverter;
import com.artofsolving.jodconverter.openoffice.connection.OpenOfficeConnection;
import com.artofsolving.jodconverter.openoffice.connection.SocketOpenOfficeConnection;
import com.artofsolving.jodconverter.openoffice.converter.OpenOfficeDocumentConverter;
import com.mysql.jdbc.log.Log4JLogger;


/**
 * OpenOffice转换PDF工具
 *  想使用它还需要dos端口启用指令:soffice -headless - 
 *  accept="socket,host=127.0.0.1,port=8100;urp;" -nofirststartwizard该指令需在OpenOffice 
 *  安装目录下的DOS窗口输入启用,且每次重启服务器都用重新启动该指令,OpenOffice这点麻烦
 */
public class DocOpenOfficeUtil {
	protected static final Logger LOG = LoggerFactory.getLogger(DocConverter.class);
	private String fileString;
	private String outputPath = "";// 输入路径 ,如果不设置就输出在默认 的位
	private String fileName;
	private File pdfFile;
	
	private File docFile;

	public DocOpenOfficeUtil(String fileString) {
		ini(fileString);
		
	}

	/**
	 * * 閲嶆柊璁剧疆file
	 * 
	 * @param fileString
	 *            32.
	 */
	public void setFile(String fileString) {
		ini(fileString);
	}

	/**
	 *  * 鍒濆锟�? 
	 * 
	 * @param fileString
	 *           
	 */
	private void ini(String fileString) {
		this.fileString = fileString;
		fileName = fileString.substring(0, fileString.lastIndexOf("."));
		docFile = new File(fileString);
		pdfFile = new File(fileName+ ".pdf");
	
	}
	
	/**
	 *  转PDF方法(OpenOffice插件) 
	 * 
	 * @param file
	 *       
	 */
	public  void doc2pdf() throws Exception {
		if (docFile.exists()) {
		//	if (!pdfFile.exists()) {
				OpenOfficeConnection connection = new SocketOpenOfficeConnection(8100);
				try {
					connection.connect();
					DocumentConverter converter = new OpenOfficeDocumentConverter(
							connection);
					converter.convert(docFile, pdfFile);
					// close the connection
					connection.disconnect();
	
					System.out.println("****pdf转换中? "+ pdfFile.getPath() + "****");
				} catch (java.net.ConnectException e) {
					e.printStackTrace();
					
					System.out.println("****pdf转换失败****");
					throw e;
				} catch (com.artofsolving.jodconverter.openoffice.connection.OpenOfficeException e) {
					e.printStackTrace();
					
					System.out.println("****pdf转换失败****");
					throw e;
				} catch (Exception e) {
					e.printStackTrace();
					
					throw e;
				}
			//} else {
			//	System.out.println("****宸茬粡杞崲涓簆df锛屼笉锟�?瑕佸啀杩涜杞寲 ****");
				
			//}
		} else {
			System.out.println("*****pdf转换失败,文件不存在****");
			
		}
	}



	static String loadStream(InputStream in) throws IOException {
		int ptr = 0;
		in = new BufferedInputStream(in);
		StringBuffer buffer = new StringBuffer();
		while ((ptr = in.read()) != -1) {
			buffer.append((char) ptr);
		}
		return buffer.toString();
	}





	
	

}

使用方法:

	
             //liberOffice
               DocConverter demo = new  DocConverter(文件路径);
	           demo.doc2pdf(); 
             //OpenOffice
	           DocOpenOfficeUtil demo2 = new  DocOpenOfficeUtil(文件路径);
	           demo2.doc2pdf();

 

docx模板工具方法:

 

	/**
	 * 替换模板字段生成新的合同docx,方法内有详细注解说明
	 * @param InputPath 模板的地址(绝对路径)例:"E:/Labor/tplfile/20181204101817265_146.docx"
	 * @param OutputPath  输出生成的docx文档的路径(绝对路径)例:"E:/Labor/tplfile/20181204101817265_146.docx"
	 * @param replaceMap  要替换的所有字段或图片的Map
	 * @return  boolean 成功true,失败false 成功后,会同时生成pdf,docx格式的两份文件,路径跟docx文档的路径一样,名字也一样,只是后缀名不同。
	 * @throws IOException
	 */
   public  boolean  templateChangeAndGenerate(String InputPath,String OutputPath,Map replaceMap)  {
	   try {

		//一行代码
		XWPFTemplate template = XWPFTemplate.compile(InputPath).render(replaceMap);
		//输出目录
		FileOutputStream out;
		
			
			out = new FileOutputStream(OutputPath);
			template.write(out);
			out.flush();
			out.close();
		
			OutputPath =  OutputPath.replace("/", "\\\\");
			//DocConverter demo = new  DocConverter(OutputPath);
			
		    //demo.doc2pdf();
			template.close();
		} catch (Exception e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
			return false;
		}
	
		return true;}

 

 

主代码涉及保密协议,我不发了。这个方案是实现了这个需求,我之前是只要用户点生成合同,就同时执行转换PDF方法。但是因为用LiberOffice的转化PDF速度太慢,而合同又是先生成并同时转换PDF,导致生成一个合同就要等半天,批量生成简直噩梦,严重影响用户体验,所以我把转换PDF方法放在了用户点击浏览后才触发生成PDF副本用来页面展示,而生成合同时,仅仅生成一份docx合同原件,不调用转换方法。然后用户体验好多了。

还有就是占存储空间,每份合同附带了分PDF副本。

坑1:如果用户上传的word模板中的字体你电脑中的字体库里没有,那么生成的PDF样式也会混乱,但只要电脑里安装了字体包就没事了

坑2:Liberoffice和OpenOffice是同源产品,如果想使用OpenOffice就必须把Liberoffice卸载干净,相反使用Liberoffice也是一样。

 

 

 

你可能感兴趣的:(java,java,poi,模板,浏览打印,编程)