服务端Word文件模板书签替换、转换文件类型的另类实现

开发 前端
市面上处理文字的的办公软件有很多,包括WPS、MSOffice、永中OFFICE,当然还有开源的openoffice、liboffice等。我们在项目开发过程中经常会遇到预览word文件,数据库中数据自动填充word模板等需求。

市面上处理文字的的办公软件有很多,包括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、安装后打开安装路径可以看到如下图 

永中软件安装目录 

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

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

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

处理word文件的代码片段,详细代码请在文后下载源码查阅 

  1. 处理word文件的代码片段,详细代码请在文后下载源码查阅     
  2.  
  3. /** 
  4.      * 将word文件转换为对应格式的文件的字节数组 
  5.      * @param type 将word文件转换成的文件格式 pdf、html\ofd\txt\xml 
  6.      * @return 
  7.      * @throws IOException 
  8.      */ 
  9.     public byte[]  convertFile(String type) throws IOException { 
  10.         int typePdf = FileConstants.TYPE_PDF; 
  11.         if("html".equals(type.toLowerCase())) {//此功能转换后乱码,后期可采用 this.workbook.saveAs("D:/2.html"); 方式存储html后,将字节返回 
  12.             typePdf= FileConstants.FILETYPE_HTML; 
  13.         }else if("ofd".equals(type.toLowerCase())) { 
  14.             typePdf= FileConstants.TYPE_OFD;    // 这个是不成功的,应该是版本太低 
  15.         }else if("txt".equals(type.toLowerCase())) { 
  16.             typePdf = FileConstants.TYPE_TXT; 
  17.         }else if("xml".equals(type.toLowerCase())) { 
  18.             typePdf = FileConstants.FILETYPE_XML; 
  19.         }else if("doc".equals(type.toLowerCase())||"xls".equals(type.toLowerCase())||"ppt".equals(type.toLowerCase())) { 
  20.             typePdf = FileConstants.TYPE_MS; 
  21.         }else if("docx".equals(type.toLowerCase())||"xlsx".equals(type.toLowerCase())||"pptx".equals(type.toLowerCase())) { 
  22.             typePdf = FileConstants.TYPE_MS_EX; 
  23.         } 
  24.          
  25.         return this.workbooks.getWorkbookAsByteArray(workbook, typePdf); 
  26.     } 
  27.  
  28. /** 
  29.      * 替换word模板中的书签 
  30.      * @param jsonObject    数据内容 {“bookmarkname”:”test“} 
  31.      */ 
  32.     public void replaceBookMark(JSONObject jsonObject) { 
  33.         BookMarks bookMarks = this.document.getBookMarks(); 
  34.         BookMark[] allBookmarks = bookMarks.getAllBookmarks(); 
  35.         for(BookMark bookMark:allBookmarks){ 
  36.             String name = bookMark.getName(); 
  37.             TextRange range = bookMark.getRange(); 
  38.             //if(name!=null)name=name.replace("PO_",""); 
  39.             String value = ""
  40.             Object o = jsonObject.get(name); 
  41.             if(o!=null){ 
  42.                 value=jsonObject.get(name).toString(); 
  43.             } 
  44.             try {  
  45.                     range.insertText(value);                
  46.             }catch (Exception e){ 
  47.                 range.insertText(value); 
  48.             } 
  49.         } 
  50.     } 
  51.  
  52. /** 
  53.      * 导出数据成excel文件 
  54.      * @param jsonObject    数据内容 {“bookmarkname”:”test“} 
  55.      */ 
  56. public byte[] exportData2File(JSONArray taskArray,int allrow) { 
  57.  

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

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

  1. /** 
  2.  * 文件传输Server端<br> 
  3.  * 功能说明: 
  4.   * @Author 空中智囊 
  5.  * @Date 2016年09月01日 
  6.  * @version 1.0 
  7.  */ 
  8. public class SocketService extends ServerSocket { 
  9.     private static final int SERVER_PORT = 8899; // 服务端端口 
  10.  
  11.     private WordUtil wordUtil=null
  12.     public SocketService() throws Exception { 
  13.         super(SERVER_PORT); 
  14.         this.wordUtil=new WordUtil(); 
  15.     } 
  16.  
  17.     /** 
  18.      * 使用线程处理每个客户端传输的文件 
  19.      * @throws Exception 
  20.      */ 
  21.     public void load() throws Exception { 
  22.         System.out.println("服务端启动,监听端口为:"+SERVER_PORT); 
  23.         while (true) { 
  24.             // server尝试接收其他Socket的连接请求,server的accept方法是阻塞式的 
  25.             Socket socket = this.accept(); 
  26.             socket.setSoTimeout(1200000); 
  27.             /** 
  28.              * 我们的服务端处理客户端的连接请求是同步进行的, 每次接收到来自客户端的连接请求后, 
  29.              * 都要先跟当前的客户端通信完之后才能再处理下一个连接请求。 这在并发比较多的情况下会严重影响程序的性能, 
  30.              * 为此,我们可以把它改为如下这种异步处理与客户端通信的方式 
  31.              */ 
  32.             // 每接收到一个Socket就建立一个新的线程来处理它 
  33.             new Thread(new Task(socket,wordUtil)).start(); 
  34.  
  35.         } 
  36.     } 
  37.     /** 
  38.      * 入口 
  39.      * @param args 
  40.      */ 
  41.     public static void main(String[] args) { 
  42.         try { 
  43.             SocketService server = new SocketService(); // 启动服务端 
  44.             server.load(); 
  45.         } catch (Exception e) { 
  46.             e.printStackTrace(); 
  47.         } 
  48.     } 
  49. /** 
  50.  * 处理客户端传输过来的文件线程类 
  51.  */ 
  52. public class Task implements Runnable { 
  53.   @Override 
  54.     public void run() { 
  55.         System.out.println("===客户端连接成功====="); 
  56.  
  57.         System.out.println("****************************************************************"); 
  58.         SimpleDateFormat format=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 
  59.         /** 
  60.          * 转换要求的格式 
  61.          */ 
  62.         try { 
  63.  
  64.             /********************************读取文件信息********************************/ 
  65.             dis = new DataInputStream(socket.getInputStream()); 
  66.             // 文件名和长度 
  67.             String fileName = dis.readUTF();//1、文件名字 
  68.             long fileLength = dis.readLong();//2、长度 
  69.             String toext = dis.readUTF();//3、扩展名 
  70.             String taskType=dis.readUTF();//4、文件操作类型 
  71.             System.out.println("针对文件的操作类型====="+taskType); 
  72.             String valueObject=dis.readUTF();//5、替换书签的值 
  73.             System.out.println(format.format(new Date())+":开始接收文件"); 
  74.             ByteArrayOutputStream bos = new ByteArrayOutputStream((int)fileLength); 
  75.             byte[] bytes = new byte[1024]; 
  76.             int length = 0; 
  77.             while((length = dis.read(bytes, 0, bytes.length)) != -1) { 
  78.                 bos.write(bytes, 0, length); 
  79.             } 
  80.             byte[] filebytes = bos.toByteArray(); 
  81.             System.out.println("原始文件大小====="+fileLength+",实际接收文件大小="+filebytes.length); 
  82.  
  83.             /********************************读取文件信息结束********************************/ 
  84.  
  85.             dos = new DataOutputStream(socket.getOutputStream()); 
  86.  
  87.             /********************************校验文件信息********************************/ 
  88.             boolean process=true
  89.             if(fileLength>0){ 
  90.             }else
  91.                 dos.writeUTF("error"); 
  92.                 dos.flush(); 
  93.  
  94.                 dos.writeUTF("文件没有任何内容,请重新传送"); 
  95.                 dos.flush(); 
  96.                 process=false
  97.             } 
  98.             if(filebytes.length!=fileLength){ 
  99.                 dos.writeUTF("error"); 
  100.                 dos.flush(); 
  101.  
  102.                 dos.writeUTF("接受文件与实际文件大小不符合,请重新传送文件"); 
  103.                 dos.flush(); 
  104.                 process=false
  105.             } 
  106.             /********************************校验文件信息结束********************************/ 
  107.  
  108.             /********************************处理文件********************************/ 
  109.             if(process){ 
  110.                 byte[] fileBytes=null
  111.                  
  112.                 this.wordUtil.openFile(filebytes,fileName);//打开院文件 
  113.                 //workbook =workbooks.createWorkbookFromByteArray(filebytes,fileName); 
  114.                 String lowerExt = toext.toLowerCase(); 
  115.                 if("convertFile".equals(taskType)){ 
  116.                     System.out.println("开始将文件["+fileName+"]转换成===="+lowerExt); 
  117.                     fileBytes=this.wordUtil.convertFile(lowerExt); 
  118.                     System.out.println(format.format(new Date())+":转换"+toext+"完成"); 
  119.                 }else if("replaceBookMark".equals(taskType)){ 
  120.                     System.out.println("开始将文件["+fileName+"]书签进行替换===="); 
  121.                     JSONObject jsonObject = JSONObject.fromObject(valueObject); 
  122.                     this.wordUtil.replaceBookMark(jsonObject); 
  123.                     fileBytes = this.wordUtil.convertFile(lowerExt); 
  124.                     System.out.println("===============替换书签完成============"); 
  125.                 }else if("exportTask".equals(taskType)) {//处理业务数据 导出任务数据 
  126.                     System.out.println("开始导出业务数据===="+valueObject); 
  127.                     ServiceUtil serviceUtil = new ServiceUtil(this.wordUtil); 
  128.                     JSONObject jsonObject = JSONObject.fromObject(valueObject); 
  129.                     fileBytes = serviceUtil.exportData2File(jsonObject.getJSONArray("datalist"), jsonObject.getInt("size")); 
  130.                      System.out.println("===============导出业务数据完成============"); 
  131.                 } 
  132.                  
  133.                  
  134.                 /********************************处理文件结束********************************/ 
  135.                 if(fileBytes==null){ 
  136.                     dos.writeUTF("error"); 
  137.                     dos.flush(); 
  138.                     dos.writeUTF("处理文件过程中错误"); 
  139.                     dos.flush(); 
  140.                     process=false
  141.                 } 
  142.  
  143.  
  144.                 /********************************返回处理过的文件********************************/ 
  145.                 if(process){ 
  146.                     dos.writeUTF("info");//文件处理完成,将信息返回到客户端 
  147.                     dos.flush(); 
  148.                     int fileBytelength = fileBytes.length;//转换后的文件长度 
  149.                     System.out.println(format.format(new Date())+":======== 服务端开始发送文件流,文件大小("+getFormatFileSize(fileBytelength)+") ========"); 
  150.                     dos.writeLong(fileBytelength); 
  151.                     dos.flush(); 
  152.                     dos.write(fileBytes, 0, fileBytelength);//将文件一起写入到输出流发送 
  153.                     dos.flush(); 
  154.                     System.out.println(format.format(new Date())+":======== 发送文件流成功 ========"); 
  155.                 } 
  156.                 /********************************返回处理过的文件完成********************************/ 
  157.  
  158.             } 
  159.         } catch (Exception e) { 
  160.             String error = e.toString(); 
  161.             System.out.println("error==================="+error); 
  162.             StackTraceElement[] stackTrace = e.getStackTrace(); 
  163.             for(StackTraceElement s:stackTrace){ 
  164.                 int lineNumber = s.getLineNumber(); 
  165.                 String methodName = s.getMethodName(); 
  166.                 String className = s.getClassName(); 
  167.                 String filename = s.getFileName(); 
  168.                 System.out.print("err:"+filename+"  "+className+"  "+methodName+"  "+lineNumber); 
  169.                 System.out.println(""); 
  170.             } 
  171.             try { 
  172.                 dos.writeUTF("error"); 
  173.                 dos.flush(); 
  174.                 dos.writeUTF("处理文件过程中错误=="+e.toString()); 
  175.                 dos.flush(); 
  176.             }catch (Exception ex){ 
  177.                 String exrror =ex.toString(); 
  178.                 System.out.println("返回数据处理错误信息==================="+exrror); 
  179.             } 
  180.  
  181.         }finally { 
  182.             System.out.println("关闭资源"); 
  183.             try { 
  184.                 if(wordUtil!=null)wordUtil.close(); 
  185.                 socket.close(); 
  186.             } catch (Exception e) { 
  187.                 String error = e.toString(); 
  188.                 System.out.println(error); 
  189.                 e.printStackTrace(); 
  190.             } 
  191.             System.out.println("****************************************************************"); 
  192.         } 
  193.  
  194.  
  195. /** 
  196.  * 文件传输Clinet端<br> 
  197.  * 功能说明: 
  198.  * @Author 空中智囊 
  199.  * @Date 2016年09月01日 
  200.  * @version 1.0 
  201.  */ 
  202. public class SocketClient extends Socket { 
  203.     public static final Logger LOGGER = LoggerFactory.getLogger(SocketClient.class); 
  204.  
  205.     private static final String SERVER_IP = "127.0.0.1"; // word文件组件处理服务IP地址 
  206.     private static final int SERVER_PORT = 8899;    // word文件组件处理服务端口 
  207.     private int soTimeout = 60000;  // 服务链接超时时间 60s 
  208.     private Socket client = this; 
  209.     private FileInputStream fis; 
  210.     private DataOutputStream dos; 
  211.     private DataInputStream dis; 
  212.     private FileOutputStream fos; 
  213.  
  214.     public SocketClient(String listenip, int listenport) throws Exception { 
  215.         super(listenip, listenport); 
  216.         this.setSoTimeout(this.soTimeout); 
  217.         LOGGER.info("Cliect[port:" + this.client.getLocalPort() + "] 成功连接服务端"); 
  218.     } 
  219.  
  220.     public SocketClient() throws Exception { 
  221.         super(SERVER_IP, SERVER_PORT); 
  222.         this.setSoTimeout(this.soTimeout); 
  223.         LOGGER.info("Cliect[port:" + this.client.getLocalPort() + "] 成功连接服务端"); 
  224.     } 
  225.  
  226.     public SocketClient(String listenip, int listenport, int soTimeout) throws Exception { 
  227.         super(listenip, listenport); 
  228.         this.setSoTimeout(soTimeout); 
  229.         LOGGER.info("Cliect[port:" + this.client.getLocalPort() + "] 成功连接服务端"); 
  230.     } 
  231.  
  232.     /** 
  233.      * 处理word文件 
  234.      * @param srcRealPath   模板word文件路径绝对地址 
  235.      * @param descRealPath  处理后的文件存放地址绝对路径 
  236.      * @param taskType      处理文件的类型 convertFile/replaceBookMark/exportTask  
  237.      * @param jsonObject    传给服务端的数据对象,这个参数可根据服务端需求进行调整 
  238.      * @return  处理结果 
  239.      */ 
  240. public JSONObject processOffice(String srcRealPath, String descRealPath, String taskType, JSONObject jsonObject) { 
  241.         JSONObject rtnObject = new JSONObject(); 
  242.         String code = "200"
  243.         String message = ""
  244.  
  245.         try { 
  246.             File file = new File(srcRealPath); 
  247.             if (!file.exists() || !file.canWrite()) { 
  248.                 code = "200"
  249.                 message = "文件不存在,或已被占用"
  250.                 rtnObject.element("code", code); 
  251.                 rtnObject.element("message", message); 
  252.                 JSONObject var41 = rtnObject; 
  253.                 return var41; 
  254.             } 
  255.  
  256.             LOGGER.info(srcRealPath + "===>" + descRealPath); 
  257.             if (file.exists() && file.canWrite()) { 
  258.                 String filename = file.getName(); 
  259.                 this.fis = new FileInputStream(file); 
  260.                 this.dos = new DataOutputStream(this.client.getOutputStream()); 
  261.                 this.dos.writeUTF(filename);//文件名字 
  262.                 this.dos.flush(); 
  263.                 this.dos.writeLong(file.length());//文件长度 
  264.                 this.dos.flush(); 
  265.                 String ext = descRealPath.substring(descRealPath.lastIndexOf(".") + 1, descRealPath.length()); 
  266.                 this.dos.writeUTF(ext);//源文件后缀名字 
  267.                 this.dos.flush(); 
  268.                 this.dos.writeUTF(taskType);//任务类型 
  269.                 this.dos.flush(); 
  270.                 if (YOZOOfficeUtil.PROCESS_TYPE_CONVERTFILE.equals(taskType)) { 
  271.                     this.dos.writeUTF(jsonObject.toString()); 
  272.                     this.dos.flush(); 
  273.                 } 
  274.  
  275.                 LOGGER.info("======== 开始向服务端传送源文件" + srcRealPath + " ========"); 
  276.                 byte[] bytes = new byte[1024]; 
  277.                 long progress = 0L; 
  278.  
  279.                 int length; 
  280.                 while((length = this.fis.read(bytes, 0, bytes.length)) != -1) { 
  281.                     this.dos.write(bytes, 0, length); 
  282.                     this.dos.flush(); 
  283.                     progress += (long)length; 
  284.                     LOGGER.info("| " + 100L * progress / file.length() + "% |"); 
  285.                 } 
  286.  
  287.                 LOGGER.info("======== 文件传输成功 (" + file.length() / 1048576L + ")M========"); 
  288.                 this.client.shutdownOutput(); 
  289.                 LOGGER.info("======== 开始转换" + ext + " ========"); 
  290.                 InputStream inputStream = this.client.getInputStream(); 
  291.                 this.dis = new DataInputStream(inputStream); 
  292.                 String result = this.dis.readUTF(); 
  293.                 if ("error".equals(result)) { 
  294.                     String reason = this.dis.readUTF(); 
  295.                     LOGGER.info(reason); 
  296.                     code = "500"
  297.                     message = reason; 
  298.                 } else if ("info".equals(result)) { 
  299.                     long l = this.dis.readLong(); 
  300.                     LOGGER.info("======== 转换" + ext + "完成,文件大小(" + l / 1048576L + ")M ========"); 
  301.                     LOGGER.info("======== 开始接受" + ext + " ========"); 
  302.                     File newFile = new File(descRealPath); 
  303.                     if (newFile.exists()) { 
  304.                         newFile.delete(); 
  305.                     } 
  306.  
  307.                     this.fos = new FileOutputStream(newFile); 
  308.                     progress = 0L; 
  309.                     bytes = new byte[1048576]; 
  310.  
  311.                     while((length = this.dis.read(bytes, 0, bytes.length)) != -1) { 
  312.                         this.fos.write(bytes, 0, length); 
  313.                         this.fos.flush(); 
  314.                     } 
  315.  
  316.                     LOGGER.info("======== 接受" + ext + "文件成功========"); 
  317.                     this.dis.close(); 
  318.                 } else { 
  319.                     code = "500"
  320.                     message = "链接被强制关闭...."
  321.                 } 
  322.             } else { 
  323.                 code = "404"
  324.                 message = "文件不存在,或已被占用:" + srcRealPath; 
  325.             } 
  326.         } catch (Exception e) { 
  327.             code = "500"
  328.             message = "客户端报错:" + e.toString(); 
  329.             LOGGER.error("异常:",e); 
  330.  
  331.         } finally { 
  332.             if (this.fis != null) { 
  333.                 try { 
  334.                     this.fis.close(); 
  335.                 } catch (Exception var38) { 
  336.                     ; 
  337.                 } 
  338.             } 
  339.  
  340.             if (this.fos != null) { 
  341.                 try { 
  342.                     this.fos.close(); 
  343.                 } catch (Exception var37) { 
  344.                     ; 
  345.                 } 
  346.             } 
  347.  
  348.             try { 
  349.                 this.client.close(); 
  350.             } catch (Exception var36) { 
  351.                 ; 
  352.             } 
  353.  
  354.         } 
  355.  
  356.         rtnObject.element("code", code); 
  357.         rtnObject.element("message", message); 
  358.         return rtnObject; 
  359.     } 
  360.  
  361.     public static void main(String[] args) { 
  362.         try { 
  363.             SocketClient socketClient = new SocketClient(); 
  364.             // 将文档转换成pdf文件 
  365.             socketClient.processOffice("D:/2.doc","D:/2.pdf",YOZOOfficeUtil.PROCESS_TYPE_CONVERTFILE,null); 
  366.  
  367.             // 将文档转换成pdf文件 
  368.             JSONObject dataObject = new JSONObject(); 
  369.             dataObject.element("bookmarkname","这个是测试呢日哦那个"); 
  370.             socketClient.processOffice("D:/2.doc","D:/2.pdf",YOZOOfficeUtil.PROCESS_TYPE_REPLACEBOOKMARK,dataObject); 
  371.         } catch (Exception e) { 
  372.             LOGGER.error("异常:",e); 
  373.  
  374.         } 
  375.  
  376.     } 

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

服务端WORD文件模板书签替换、转换文件类型的另类实现(源码)
组件启动脚本

nohup ./ofdServer.sh &

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

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

7、资源下载

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

链接:https://pan.baidu.com/s/1_ZgjoX_nuv3a7_SKkJ_D7w 提取码: hn2r 

 服务端资源内容 

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

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

链接:https://pan.baidu.com/s/1mtabGY87RuAGGkwKrBIvfQ 提取码: mqxf 


客户端资源文件内容 

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

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

 

责任编辑:未丽燕 来源: 今日头条
相关推荐

2009-11-16 12:17:46

PHP上传文件类型

2023-11-03 18:09:31

sed文件属性

2009-09-11 08:47:03

Linux系统批量替换文件Linux

2010-03-10 09:27:36

Linux链接文件类型

2010-01-05 15:32:33

2010-03-01 15:34:38

Linux文件类型

2012-05-21 23:43:31

Java

2009-10-21 10:50:59

Linux文件类型

2010-01-12 11:37:06

Linux常见文件

2009-11-09 13:31:09

WCF服务端配置

2009-07-24 10:23:07

WORD文件转换PDFASP.NET

2010-04-02 13:15:01

Oracle跟踪

2010-05-21 10:02:56

2011-06-01 10:48:28

Android系统

2017-06-09 06:14:41

2009-08-12 16:44:13

.NET文件类型

2011-05-25 16:44:01

JavaScriptCSS

2021-02-05 08:03:52

Java

2019-04-12 14:26:17

Linux命令文件

2010-02-24 15:42:03

WCF服务端安全
点赞
收藏

51CTO技术栈公众号