項目中有一個需求:實現文件(主要是Office文件)的在線預覽,根據前端需求,Office文件需要轉換成pdf或者html方可在瀏覽器中打開預覽,那么后端需要將文件轉為pdf/格式返回地址給前端。目前,了解到的解決方案大概有兩種,一種是基於Apache組織下的開源項目:OpenOffice,一種是使用jacob橋接方案。兩種方案均可實現需求,但是在使用過程中遇到些許波折與坑,寫在這里,與大家共勉。
方案一、OpenOffice
OpenOffice.org 是一套跨平台的辦公室軟件套件,能在Windows、Linux、MacOS X (X11)和 Solaris 等操作系統上執行。它與各個主要的辦公室軟件套件兼容。OpenOffice.org 是自由軟件,任何人都可以免費下載、使用及推廣它(來自 :百度百科)。
因為他是開源的,所以提供了可供java等開發語言開發的API,在此基礎上,我們可以使用它來完成一些功能的轉換、開發。
開發流程:
(1)安裝OpenOffice
因為用到其功能,因此首先需要安裝它,下載地址:https://cwiki.apache.org/confluence/display/OOOUSERS/AOO+4.1.6+Release+Notes
安裝過程簡單,直接下一步即可。
(2)SpringBoot集成Open Office
首先引入jar包:如下是必須的
因為有些jar包,maven倉庫找不到,所以我把它放在了項目中,然后在pom文件中進行了引用
這里需要注意的是,引入jar包后,還需要在intelliJ Idea中設置project Structer引入。
(3)工具類編寫
這里支持的是所有的Office文件以及txt文件,包括03版和07版均兼容。
核心代碼:

1 /** 2 * 轉換文件成pdf 3 * 4 * @param : 5 * @throws IOException 6 */ 7 public static void fileTopdfbak(File docInputFile, String toFilePath, String type) { 8 String timesuffix = UUID.randomUUID().toString(); 9 String pdfFileName = null; 10 if("doc".equals(type)||"docx".equals(type)){ 11 pdfFileName = timesuffix.concat(".pdf"); 12 }else if("xls".equals(type)||"xlsx".equals(type)){ 13 pdfFileName = timesuffix.concat(".pdf"); 14 }else if("ppt".equals(type) || "pptx".equals(type)){ 15 pdfFileName = timesuffix.concat(".pdf"); 16 }else if("txt".equals(type)){ 17 pdfFileName = timesuffix.concat(".pdf"); 18 }else{ 19 return ; 20 } 21 22 String realPath=toFilePath+pdfFileName; 23 File pdfOutputFile = new File(realPath); 24 if (pdfOutputFile.exists()){ 25 pdfOutputFile.delete(); 26 } 27 String contextpath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort(); 28 String url = contextpath +EnumUtil.CONVERT_PDF_PATH.getCode()+pdfFileName; 29 System.out.println("文件服務器路徑:"+url); 30 pdfOutputFile.createNewFile(); 31 // docInputFile.createNewFile(); 32 OpenOfficeConnection connection = new SocketOpenOfficeConnection("127.0.0.1",8100); 33 System.out.println("connection:"+connection); 34 connection.connect(); 35 // convert 36 DocumentConverter converter = new StreamOpenOfficeDocumentConverter(connection); 37 if(null!=docInputFile){ 38 converter.convert(docInputFile, pdfOutputFile); 39 } 40 connection.disconnect(); 41 // 轉換完之后刪除word文件 42 // docInputFile.delete(); 43 }
這里需要注意的是:在使用之前需要先啟動安裝的Open Office服務,windows啟動命令:
在安裝該軟件的地方進入
openOffice4/program 目錄,然后cmd執行:
soffice -headless -accept="socket,host=127.0.0.1,port=8100;urp;" -nofirststartwizard
經過測試,該方法可用,
但是,項目中有個需求,大文件(超過2G)也需要在線預覽,於是,試了下800兆的文件,測試過程中,出現了問題,800兆的文件出現了轉換失敗的情況,而且觀察內存使用情況,發現內存使用較高,運行慢,出錯后內存不會釋放,報錯信息:
意思大概是內存分配錯誤,運行OpenOffice的可視化工具,直接用這個軟件好像800M的ppt也打不開,后來查資料得知,通過設置可以增加內存,但是嘗試了幾次,發現可設置的內存最大到256M
通過網上查資料,發現根本沒有解決方法,因此該方案不得不放棄,如果大家有什么好的解決方案,歡迎留言討論,不勝感激!
方案二、使用jacob
首先,我們需要知道什么是jacob,官方文檔中有一句話:JACOB is a JAVA-COM Bridge that allows you to call COM Automation components from Java. It uses JNI to make native calls to the COM libraries. JACOB runs on x86 and x64 environments supporting 32 bit and 64 bit JVMs,翻譯過了就是:Jacob是一個Java-COM橋,允許您調用COM自動化組件。不難理解,該方案提供了java和COM(ComponentObjectModel,組件對象模型)之間的一個橋梁,個人理解類似於數據庫提供的驅動。
該方案的實現過程就是通過java來調用window中的Office來達到文件格式轉換的目的,類似於我們打開一個文檔,選擇另存為其他格式的過程。因為在Windows操作平台下,眾多以COM形式提供的組件模塊,如DirectX多媒體軟件包、OLEDB/ADO數據庫組件系統等,極大地豐富了操作系統的功能。由於COM機制允許任意兩組件之間相互通信而不必關心是在何種計算機上的何種操作系統下運行,也不用關心組件是使用何種語言編制的,這使COM技術擁有了強大的生命力。而我們需要的是Office的組件,因此,需要jacob來建立windows和Java之間的橋梁。
開發過程:
(1)下載jacob.jar及相關dll文件
因為在maven倉庫中有該jar包,直接引入,也可直接下載:http://www.java2s.com/Code/Jar/j/Downloadjacob1143jar.htm
另外,還需要jacob-1.14.3-x64.dll(系統64位),jacob-1.14.3-x86.dll(系統32位),可以百度自行下載,這里需要注意的是版本一定要和jar包的版本保持一致。將下載好的文件拷貝一下兩個目錄:
C:\Windows\System32(64位),D:\Program Files\Java\jre1.8.0_131\bin jre的安裝目錄,注意不是jdk里面的jre,網上有說將拷貝至jdk下面的jre/bin下的,估計是環境不一樣,我試過會報錯。
(2)核心代碼

/** * * @Title: ppt2PDF * @Description: 轉換ppt為office * @param @param inputFile * @param @param pdfFile * @param @return 設定文件 * @return boolean 返回類型 * @throws */ public static boolean ppt2PDF(String inputFile,String pdfFile){ try{ System.out.println("PowerPoint.Application............"); ActiveXComponent app = new ActiveXComponent("PowerPoint.Application"); //app.setProperty("Visible", msofalse); Dispatch ppts = app.getProperty("Presentations").toDispatch(); Dispatch ppt = Dispatch.call(ppts, "Open", inputFile, true,//ReadOnly true,//Untitled指定文件是否有標題 false//WithWindow指定文件是否可見 ).toDispatch(); Dispatch.call(ppt, "SaveAs", pdfFile, ppSaveAsPDF ); Dispatch.call(ppt, "Close"); app.invoke("Quit"); return true; }catch(Exception e){ return false; } } /** * * @Title: word2PDF * @Description: 轉換word文檔為pdf * @param @param inputFile * @param @param pdfFile * @param @return 設定文件 * @return boolean 返回類型 * @throws */ public static boolean word2PDF(String inputFile,String pdfFile){ try{ //打開word應用程序 ActiveXComponent app = new ActiveXComponent("Word.Application"); //設置word不可見 app.setProperty("Visible", false); //獲得word中所有打開的文檔,返回Documents對象 Dispatch docs = app.getProperty("Documents").toDispatch(); //調用Documents對象中Open方法打開文檔,並返回打開的文檔對象Document Dispatch doc = Dispatch.call(docs, "Open", inputFile, false, true ).toDispatch(); //調用Document對象的SaveAs方法,將文檔保存為pdf格式 /* Dispatch.call(doc, "SaveAs", pdfFile, wdFormatPDF //word保存為pdf格式宏,值為17 ); */ Dispatch.call(doc, "ExportAsFixedFormat", pdfFile, wdFormatPDF //word保存為pdf格式宏,值為17 ); //關閉文檔 Dispatch.call(doc, "Close",false); //關閉word應用程序 app.invoke("Quit", 0); return true; }catch(Exception e){ return false; } } /** * * @Title: excel2PDF * @Description: 轉換excel為PDF * @param @param inputFile * @param @param pdfFile * @param @return 設定文件 * @return boolean 返回類型 * @throws */ public static boolean excel2PDF(String inputFile,String pdfFile){ try{ ActiveXComponent app = new ActiveXComponent("Excel.Application"); app.setProperty("Visible", false); Dispatch excels = app.getProperty("Workbooks").toDispatch(); Dispatch excel = Dispatch.call(excels, "Open", inputFile, false, true ).toDispatch(); Dispatch.call(excel, "ExportAsFixedFormat", xlTypePDF, pdfFile ); Dispatch.call(excel, "Close",false); app.invoke("Quit"); return true; }catch(Exception e){ return false; } }
測試結果:直接上800M的ppt,測試結果:
經過測試,只要是微軟Office能打開的文件,都可以完成轉換。
(4)問題及坑
開發過程中,發現測試在本地可行,發布到服務器報錯,內容如下:
java.lang.NoSuchMethodError: com.jacob.com.Dispatch.call(Lcom/jacob/com/Dispatch;Ljava/lang/String;[Ljava/lang/Object;)Lcom/jacob/com/Variant;
at com.thupdi.project.utils.OfficeToPDFUtils.ppt2PDF(OfficeToPDFUtils.java:99)
at com.thupdi.project.utils.OfficeToPDFUtils.convert2PDF(OfficeToPDFUtils.java:69)
方法找不到,應該是缺少jar包或者jar包沖突導致的。
解決方案,找到之前引用的jar包,因為之前在項目中導入過jacob.jar包,所以導致沖突。還有一種可能就是maven打包的時候沒有把項目中引用的jar包(非maven引入)一並打入war/jar包,導致找不到方法。按這種方法順利解決的問題,大家如果有這個問題,可以按這兩個方法嘗試解決。