文章最后提供源码下载地址

市面上处理文字的的办公软件有很多,包括WPS、MSOffice、永中OFFICE,当然还有开源的openoffice、liboffice等。我们在项目开发过程中经常会遇到预览WORD文件,数据库中数据自动填充word模板等需求。现在能够满足以上需求的技术有很多,服务端可通过POI\aspose等处理,也可通过客户端调用OFFICE组件处理,本人曾经在这方便做了很多测试,最终发现兼容性最好的、接口对JAVA程序员最友好的就属永中OFFICE,因为它基本就是JAVA实现的,使用起来非常方便。

我的测试环境使用的是永中2016版本,它运行要求JRE1.6,且我发现它应该是对JRE进行过重构,按永中SDK要求编写代码通过自ORACAL官网下载的jdk1.6编译后运行是失败的,现在都2021年了,我们的项目绝大多数都JDK1.8以上版本了,那么怎么让SDK兼容我们的项目呢?怎么实现标题中提到的两个需求呢?下面我说说我的处理方法吧:

1、下载永中软件并安装(官网下载即可)

2、安装后打开安装路径可以看到如下图

word模板转html(服务端WORD文件模板书签替换)(1)

永中软件安装目录

JRE:即永中软件的运行环境

Yozo_Office.jar: 即永中为开发者提供的SDK,可以将jar导入到工程中

3、编写WORD文件处理服务组件

处理word文件的代码片段,详细代码请在文后下载源码查阅 /** * 将word文件转换为对应格式的文件的字节数组 * @param type 将word文件转换成的文件格式 pdf、html\ofd\txt\xml * @return * @throws IOException */ public byte[] convertFile(String type) throws IOException { int typePdf = FileConstants.TYPE_PDF; if("html".equals(type.toLowerCase())) {//此功能转换后乱码,后期可采用 this.Workbook.saveAs("D:/2.html"); 方式存储html后,将字节返回 typePdf= FileConstants.FILETYPE_HTML; }else if("ofd".equals(type.toLowerCase())) { typePdf= FileConstants.TYPE_OFD; // 这个是不成功的,应该是版本太低 }else if("txt".equals(type.toLowerCase())) { typePdf = FileConstants.TYPE_TXT; }else if("xml".equals(type.toLowerCase())) { typePdf = FileConstants.FILETYPE_XML; }else if("doc".equals(type.toLowerCase())||"xls".equals(type.toLowerCase())||"ppt".equals(type.toLowerCase())) { typePdf = FileConstants.TYPE_MS; }else if("docx".equals(type.toLowerCase())||"xlsx".equals(type.toLowerCase())||"pptx".equals(type.toLowerCase())) { typePdf = FileConstants.TYPE_MS_EX; } return this.workbooks.getWorkbookAsByteArray(workbook, typePdf); } /** * 替换word模板中的书签 * @param jsonObject 数据内容 {“bookmarkname”:”test“} */ public void replaceBookMark(JSONObject jsonObject) { BookMarks bookMarks = this.document.getBookMarks(); BookMark[] allBookmarks = bookMarks.getAllBookmarks(); for(BookMark bookMark:allBookmarks){ String name = bookMark.getName(); TextRange range = bookMark.getRange(); //if(name!=null)name=name.replace("PO_",""); String value = ""; Object o = jsonObject.get(name); if(o!=null){ value=jsonObject.get(name).toString(); } try { range.insertText(value); }catch (Exception e){ range.insertText(value); } } } /** * 导出数据成excel文件 * @param jsonObject 数据内容 {“bookmarkname”:”test“} */ public byte[] exportData2File(JSONArray taskArray,int allrow) { }

4、(重点)解决word文件处理组件与我们的项目文件交互问题

本人通过SOCKET即时通讯服务解决数据交互问题

/** * 文件传输Server端<br> * 功能说明: * @Author 空中智囊 * @Date 2016年09月01日 * @version 1.0 */ public class socketService extends ServerSocket { private static final int SERVER_PORT = 8899; // 服务端端口 private WordUtil wordUtil=null; public SocketService() throws Exception { super(SERVER_PORT); this.wordUtil=new WordUtil(); } /** * 使用线程处理每个客户端传输的文件 * @throws Exception */ public void load() throws Exception { System.out.println("服务端启动,监听端口为:" SERVER_PORT); while (true) { // server尝试接收其他Socket的连接请求,server的accept方法是阻塞式的 Socket socket = this.accept(); socket.setSoTimeout(1200000); /** * 我们的服务端处理客户端的连接请求是同步进行的, 每次接收到来自客户端的连接请求后, * 都要先跟当前的客户端通信完之后才能再处理下一个连接请求。 这在并发比较多的情况下会严重影响程序的性能, * 为此,我们可以把它改为如下这种异步处理与客户端通信的方式 */ // 每接收到一个Socket就建立一个新的线程来处理它 new Thread(new Task(socket,wordUtil)).start(); } } /** * 入口 * @param args */ public static void main(String[] args) { try { SocketService server = new SocketService(); // 启动服务端 server.load(); } catch (Exception e) { e.printStackTrace(); } } } /** * 处理客户端传输过来的文件线程类 */ public class Task implements Runnable { @Override public void run() { System.out.println("===客户端连接成功====="); System.out.println("****************************************************************"); SimpleDateFormat format=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); /** * 转换要求的格式 */ try { /********************************读取文件信息********************************/ dis = new DataInputStream(socket.getInputStream()); // 文件名和长度 String fileName = dis.readUTF();//1、文件名字 long fileLength = dis.readLong();//2、长度 String toext = dis.readUTF();//3、扩展名 String taskType=dis.readUTF();//4、文件操作类型 System.out.println("针对文件的操作类型=====" taskType); String valueObject=dis.readUTF();//5、替换书签的值 System.out.println(format.format(new Date()) ":开始接收文件"); ByteArrayOutputStream bos = new ByteArrayOutputStream((int)fileLength); byte[] bytes = new byte[1024]; int length = 0; while((length = dis.read(bytes, 0, bytes.length)) != -1) { bos.write(bytes, 0, length); } byte[] filebytes = bos.toByteArray(); System.out.println("原始文件大小=====" fileLength ",实际接收文件大小=" filebytes.length); /********************************读取文件信息结束********************************/ dos = new DataOutputStream(socket.getOutputStream()); /********************************校验文件信息********************************/ boolean process=true; if(fileLength>0){ }else{ dos.writeUTF("error"); dos.flush(); dos.writeUTF("文件没有任何内容,请重新传送"); dos.flush(); process=false; } if(filebytes.length!=fileLength){ dos.writeUTF("error"); dos.flush(); dos.writeUTF("接受文件与实际文件大小不符合,请重新传送文件"); dos.flush(); process=false; } /********************************校验文件信息结束********************************/ /********************************处理文件********************************/ if(process){ byte[] fileBytes=null; this.wordUtil.openFile(filebytes,fileName);//打开院文件 //workbook =workbooks.createWorkbookFromByteArray(filebytes,fileName); String lowerExt = toext.toLowerCase(); if("convertFile".equals(taskType)){ System.out.println("开始将文件[" fileName "]转换成====" lowerExt); fileBytes=this.wordUtil.convertFile(lowerExt); System.out.println(format.format(new Date()) ":转换" toext "完成"); }else if("replaceBookMark".equals(taskType)){ System.out.println("开始将文件[" fileName "]书签进行替换===="); JSONObject jsonObject = JSONObject.fromObject(valueObject); this.wordUtil.replaceBookMark(jsonObject); fileBytes = this.wordUtil.convertFile(lowerExt); System.out.println("===============替换书签完成============"); }else if("exportTask".equals(taskType)) {//处理业务数据 导出任务数据 System.out.println("开始导出业务数据====" valueObject); ServiceUtil serviceUtil = new ServiceUtil(this.wordUtil); JSONObject jsonObject = JSONObject.fromObject(valueObject); fileBytes = serviceUtil.exportData2File(jsonObject.getJSONArray("datalist"), jsonObject.getInt("size")); System.out.println("===============导出业务数据完成============"); } /********************************处理文件结束********************************/ if(fileBytes==null){ dos.writeUTF("error"); dos.flush(); dos.writeUTF("处理文件过程中错误"); dos.flush(); process=false; } /********************************返回处理过的文件********************************/ if(process){ dos.writeUTF("info");//文件处理完成,将信息返回到客户端 dos.flush(); int fileBytelength = fileBytes.length;//转换后的文件长度 System.out.println(format.format(new Date()) ":======== 服务端开始发送文件流,文件大小(" getFormatFileSize(fileBytelength) ") ========"); dos.writeLong(fileBytelength); dos.flush(); dos.write(fileBytes, 0, fileBytelength);//将文件一起写入到输出流发送 dos.flush(); System.out.println(format.format(new Date()) ":======== 发送文件流成功 ========"); } /********************************返回处理过的文件完成********************************/ } } catch (Exception e) { String error = e.toString(); System.out.println("error===================" error); StackTraceElement[] stackTrace = e.getStackTrace(); for(StackTraceElement s:stackTrace){ int lineNumber = s.getLineNumber(); String methodName = s.getMethodName(); String className = s.getClassName(); String filename = s.getFileName(); System.out.print("err:" filename " " className " " methodName " " lineNumber); System.out.println(""); } try { dos.writeUTF("error"); dos.flush(); dos.writeUTF("处理文件过程中错误==" e.toString()); dos.flush(); }catch (Exception ex){ String exrror =ex.toString(); System.out.println("返回数据处理错误信息===================" exrror); } }finally { System.out.println("关闭资源"); try { if(wordUtil!=null)wordUtil.close(); socket.close(); } catch (Exception e) { String error = e.toString(); System.out.println(error); e.printStackTrace(); } System.out.println("****************************************************************"); } } /** * 文件传输Clinet端<br> * 功能说明: * @Author 空中智囊 * @Date 2016年09月01日 * @version 1.0 */ public class SocketClient extends Socket { public static final Logger LOGGER = LoggerFactory.getLogger(SocketClient.class); private static final String SERVER_IP = "127.0.0.1"; // word文件组件处理服务IP地址 private static final int SERVER_PORT = 8899; // word文件组件处理服务端口 private int soTimeout = 60000; // 服务链接超时时间 60s private Socket client = this; private FileInputStream fis; private DataOutputStream dos; private DataInputStream dis; private FileOutputStream fos; public SocketClient(String listenip, int listenport) throws Exception { super(listenip, listenport); this.setSoTimeout(this.soTimeout); LOGGER.info("Cliect[port:" this.client.getLocalPort() "] 成功连接服务端"); } public SocketClient() throws Exception { super(SERVER_IP, SERVER_PORT); this.setSoTimeout(this.soTimeout); LOGGER.info("Cliect[port:" this.client.getLocalPort() "] 成功连接服务端"); } public SocketClient(String listenip, int listenport, int soTimeout) throws Exception { super(listenip, listenport); this.setSoTimeout(soTimeout); LOGGER.info("Cliect[port:" this.client.getLocalPort() "] 成功连接服务端"); } /** * 处理word文件 * @param srcRealPath 模板word文件路径绝对地址 * @param descRealPath 处理后的文件存放地址绝对路径 * @param taskType 处理文件的类型 convertFile/replaceBookMark/exportTask * @param jsonObject 传给服务端的数据对象,这个参数可根据服务端需求进行调整 * @return 处理结果 */ public JSONObject processOffice(String srcRealPath, String descRealPath, String taskType, JSONObject jsonObject) { JSONObject rtnObject = new JSONObject(); String code = "200"; String message = ""; try { File file = new File(srcRealPath); if (!file.exists() || !file.canWrite()) { code = "200"; message = "文件不存在,或已被占用"; rtnObject.element("code", code); rtnObject.element("message", message); JSONObject var41 = rtnObject; return var41; } LOGGER.info(srcRealPath "===>" descRealPath); if (file.exists() && file.canWrite()) { String filename = file.getName(); this.fis = new FileInputStream(file); this.dos = new DataOutputStream(this.client.getOutputStream()); this.dos.writeUTF(filename);//文件名字 this.dos.flush(); this.dos.writeLong(file.length());//文件长度 this.dos.flush(); String ext = descRealPath.substring(descRealPath.lastIndexOf(".") 1, descRealPath.length()); this.dos.writeUTF(ext);//源文件后缀名字 this.dos.flush(); this.dos.writeUTF(taskType);//任务类型 this.dos.flush(); if (YOZOOfficeUtil.PROCESS_TYPE_CONVERTFILE.equals(taskType)) { this.dos.writeUTF(jsonObject.toString()); this.dos.flush(); } LOGGER.info("======== 开始向服务端传送源文件" srcRealPath " ========"); byte[] bytes = new byte[1024]; long progress = 0L; int length; while((length = this.fis.read(bytes, 0, bytes.length)) != -1) { this.dos.write(bytes, 0, length); this.dos.flush(); progress = (long)length; LOGGER.info("| " 100L * progress / file.length() "% |"); } LOGGER.info("======== 文件传输成功 (" file.length() / 1048576L ")M========"); this.client.shutdownOutput(); LOGGER.info("======== 开始转换" ext " ========"); InputStream inputStream = this.client.getInputStream(); this.dis = new DataInputStream(inputStream); String result = this.dis.readUTF(); if ("error".equals(result)) { String reason = this.dis.readUTF(); LOGGER.info(reason); code = "500"; message = reason; } else if ("info".equals(result)) { long l = this.dis.readLong(); LOGGER.info("======== 转换" ext "完成,文件大小(" l / 1048576L ")M ========"); LOGGER.info("======== 开始接受" ext " ========"); File newFile = new File(descRealPath); if (newFile.exists()) { newFile.delete(); } this.fos = new FileOutputStream(newFile); progress = 0L; bytes = new byte[1048576]; while((length = this.dis.read(bytes, 0, bytes.length)) != -1) { this.fos.write(bytes, 0, length); this.fos.flush(); } LOGGER.info("======== 接受" ext "文件成功========"); this.dis.close(); } else { code = "500"; message = "链接被强制关闭...."; } } else { code = "404"; message = "文件不存在,或已被占用:" srcRealPath; } } catch (Exception e) { code = "500"; message = "客户端报错:" e.toString(); LOGGER.error("异常:",e); } finally { if (this.fis != null) { try { this.fis.close(); } catch (Exception var38) { ; } } if (this.fos != null) { try { this.fos.close(); } catch (Exception var37) { ; } } try { this.client.close(); } catch (Exception var36) { ; } } rtnObject.element("code", code); rtnObject.element("message", message); return rtnObject; } public static void main(String[] args) { try { SocketClient socketClient = new SocketClient(); // 将文档转换成pdf文件 socketClient.processOffice("D:/2.doc","D:/2.pdf",YOZOOfficeUtil.PROCESS_TYPE_CONVERTFILE,null); // 将文档转换成pdf文件 JSONObject dataObject = new JSONObject(); dataObject.element("bookmarkname","这个是测试呢日哦那个"); socketClient.processOffice("D:/2.doc","D:/2.pdf",YOZOOfficeUtil.PROCESS_TYPE_REPLACEBOOKMARK,dataObject); } catch (Exception e) { LOGGER.error("异常:",e); } } }

5、启动word文件处理组件服务端

word模板转html(服务端WORD文件模板书签替换)(2)

组件启动脚本

nohup ./ofdServer.sh &

6、调用服务端对word文件处理

public static void main(String[] args) { try { SocketClient socketClient = new SocketClient(); // 将文档转换成pdf文件 socketClient.processOffice("D:/2.doc","D:/2.pdf",YOZOOfficeUtil.PROCESS_TYPE_CONVERTFILE,null); // 替换模板中的书签值,word中插入书签自行百度 JSONObject dataObject = new JSONObject(); dataObject.element("bookmarkname","这个是测试呢日哦那个"); socketClient.processOffice("D:/2.doc","D:/3.doc",YOZOOfficeUtil.PROCESS_TYPE_REPLACEBOOKMARK,dataObject); } catch (Exception e) { LOGGER.error("异常:",e); } }

7、资源下载

word文件处理组件服务端(开箱即用):

链接: https:///s/1_ZgjoX_nuv3a7_SKkJ_D7w 提取码: hn2r

word模板转html(服务端WORD文件模板书签替换)(3)

服务端资源内容

将文件复制到linux服务器,并解压,执行 ./ofdServer.sh ,输出:服务端启动,监听端口为:8899,即运行成功

word文件处理组件客户端(开箱即用processOffice):

链接: https:///s/1mtabGY87RuAGGkwKrBIvfQ 提取码: mqxf

word模板转html(服务端WORD文件模板书签替换)(4)

客户端资源文件内容

将源文件复制到项目指定包名,运行SocketClient.java中的main方法,可查看运行结果。

最重要的一点:服务器要安装永中OFFICE客户端

,