1. 程式人生 > >SpringBoot-檔案線上預覽解決方案-基於OpenOffice及jacob

SpringBoot-檔案線上預覽解決方案-基於OpenOffice及jacob

專案中有一個需求:實現檔案(主要是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     }
View Code

這裡需要注意的是:在使用之前需要先啟動安裝的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;
        }
    }
View Code

測試結果:直接上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包,導致找不到方法。按這種方法順利解決的問題,大家如果有這個問題,可以按這兩個方法嘗試解