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:WindowsSystem32(64位),D:Program FilesJavajre1.8.0_131in 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包,导致找不到方法。按这种方法顺利解决的问题,大家如果有这个问题,可以按这两个方法尝试解决。

原文地址:https://www.cnblogs.com/10158wsj/p/11051278.html