资讯专栏INFORMATION COLUMN

php中调用Java实现word文档的预览

leejan97 / 2303人阅读

摘要:在下一切运行正常,但是到下文件的文件名和生成路径就会发生变化,这里的不会被当作路径分隔符了,而是当作文件名的一部分,其实修改起来也很简单创建另一个线程启动服务,老是会出现无法连接服务的异常。

php预览word文档的实现 以及实现过程中遇到的各种坑
在做软件工程的课程设计的时候,我们小组选择做一个资料分享网站,网站最重要的功能当然就是上传文件和下载文件。但是这中间就需要一个比较重要的过程:预览。

预览最终结果是一张长图,很长很长的png图片。大致可以分为下面这几个步骤:

将WORD文档转为PDF

将PDF拆分,毕竟只是预览,而不是查看全部,这里我设置的是预览10页

将PDF按页转换为PNG图片

将所有的PNG图片合并成一张长图

四个步骤,分别需要用到不同的工具:

第一步:对于WORD转PDF,在度娘的帮助下,我们决定使用Java语言实现这一功能。使用开源的openoffice+jodconverter来对WORD进行转换。

首先我们要考虑php如何调用Java,很幸运,有一个叫做JavaBridge的东西为我们解决了这个问题,JavaBridge的使用在百度中有大量的博客教程(ps:虽然我对国内这些博客互相抄袭,还错误百出很看不习惯,但确实也有好的博客,并且数量很多);

然后就是要安装OpenOffice,这个很简单,无论是Windows还是Linux都不难(直接百度搜索openoffice安装即可);
最后下载jodconverter的jar包,编写程序将WORD转为PDF。

源码如下

import java.io.File;
import java.io.IOException;

import com.artofsolving.jodconverter.DocumentConverter;
import com.artofsolving.jodconverter.openoffice.connection.OpenOfficeConnection;
import com.artofsolving.jodconverter.openoffice.connection.SocketOpenOfficeConnection;
import com.artofsolving.jodconverter.openoffice.converter.OpenOfficeDocumentConverter;

public class PDFConverter {
    /**
     * 将WORD文档转换为PDF
     * @param srcPath WORD文档路径
     * @param desPath 目标PDF保存路径
     * @param pages 转换页数
     * @throws IOException
     */
    public void Word2Pdf(String srcPath, String desPath) throws IOException {
        // 源文件目录
        File inputFile = new File(srcPath);
        if (!inputFile.exists()) {
            System.out.println("源文件不存在!");
            System.out.println(srcPath + ", " + desPath);
            return;
        }
        
        // 输出文件目录
        File outputFile = new File(desPath);
        if (!outputFile.getParentFile().exists()) {
            outputFile.getParentFile().mkdirs();
        }
        // 调用openoffice服务线程
//        String command = "C:Program Files (x86)OpenOffice 4programsoffice.exe -headless -accept="socket,host=127.0.0.1,port=8100;urp;"";
        String command = "/opt/openoffice4/program/soffice -headless -accept="socket,host=127.0.0.1,port=8100;urp;" -nofirststartwizard";
        Process p = Runtime.getRuntime().exec(command);
        // 连接openoffice服务
        OpenOfficeConnection connection = new SocketOpenOfficeConnection(
                "127.0.0.1", 8100);
        connection.connect();
        // 转换word到pdf
        DocumentConverter converter = new OpenOfficeDocumentConverter(
                connection);
        converter.convert(inputFile, outputFile);
        // 关闭连接
        connection.disconnect();
        // 关闭进程
        p.destroy();
        System.out.println("转换完成!");
    }
}
在程序中我们创建一个线程来打开OpenOffice的服务Process p = Runtime.getRuntime().exec(command);这条代码的作用相当于在命令行(终端)输入command字符串,而command字符串就是我们启动openoffice服务的命令。后面连接openoffice然后利用它提供的接口将WORD转为PDF即可。(注意:注释起来的command是Windows下的启动命令,因为Windows系统和linux系统中openoffice安装路径不同,所以需要使用不同的路径启动服务,所以在安装OpenOffice时一定要注意安装路径
第二步:将一个大的PDF拆分为小的PDF,可能是PHP这方面的支持不够,也可能是我对Java很有好感,这一步我选择的是使用Apache的pdfbox结合Java实现的。

在apache官网中找到pdfbox,下载fontbox-2.0.15.jar、pdfbox-2.0.15.jar、commons-logging-1.2.jar这三个jar包,但是我在apahce官网上面并没有找到最后一个jar包,是在别人分享的百度云盘里面下载的,所以最后我会把我用到的所有jar包上传到百度云并提供永久下载链接;获得这三个jar包后编写程序;

源码如下

/**
 * 将一个大pdf拆分为小pdf
 * @param src 大PDF路径
 * @param dest 小PDF保存路径
 * @param pages 拆分页数
 */
public void split(String src, String dest, int pages) {
    File srcFile = new File(src);
    if(!srcFile.exists()) {
        System.out.println("原文件不存在");
        return;
    }
    
    // 如果目标路径的父目录不存在,则创建
    File destFile = new File(dest);
    if(!destFile.getParentFile().exists()) {
        destFile.getParentFile().mkdirs();
    }
    
    try {
        System.out.println("开始拆分");
        // 加载原PDF文件
        PDDocument pdf = PDDocument.load(srcFile, MemoryUsageSetting.setupTempFileOnly());
        // 获取原PDF文件总页数
        int pageCount = pdf.getPages().getCount();
        
        // 当所需要的页数大于总页数时,按最大总页数进行拆分
        if(pages > pageCount) {
            pages = pageCount;
        }
        
        PDDocument newPdf = new PDDocument();
        for(int i=0;i
第三步:将PDF转为PNG图片,很幸运在pdfbox中提供了这样的功能,所以这一步不需要任何工具包
直接就可以编写代码
/**
 * 将pdf转为png图片
 * @param src 原pdf路径
 * @param dest 图片的父目录
 */
public void Pdf2Png(String src, String dest) {
    File srcFile = new File(src);
    if(!srcFile.exists()) {
        System.out.println("源文件不存在");
        return;
    }
    
    File destFile = new File(dest);
    if(!destFile.exists()) {
        destFile.mkdirs();
    }
    
    try {
        PDDocument doc = PDDocument.load(srcFile);
        PDFRenderer renderer = new PDFRenderer(doc);
        int pageCount = doc.getNumberOfPages();
        for(int i=0;i

由于第二步和第三步用到了同样的外部jar包,所以我把它们放在同一个项目,下面是完整的项目

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;

import org.apache.pdfbox.io.MemoryUsageSetting;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;

public class PDFProcess {
    /**
     * 将pdf转为png图片
     * @param src 原pdf路径
     * @param dest 图片的父目录
     */
    public void Pdf2Png(String src, String dest) {
        File srcFile = new File(src);
        if(!srcFile.exists()) {
            System.out.println("源文件不存在");
            return;
        }
        
        File destFile = new File(dest);
        if(!destFile.exists()) {
            destFile.mkdirs();
        }
        
        try {
            PDDocument doc = PDDocument.load(srcFile);
            PDFRenderer renderer = new PDFRenderer(doc);
            int pageCount = doc.getNumberOfPages();
            for(int i=0;i pageCount) {
                pages = pageCount;
            }
            
            PDDocument newPdf = new PDDocument();
            for(int i=0;i
最后一个合并PNG,这个要求直接使用php即可实现,不过需要GD2扩展库,如果是Windows下这个扩展是自带的,Linux下需要自己安装扩展
 $width) {
                $width = $arr[$i]["size"][0];
            }
            $height += $arr[$i]["size"][1];
        }

        $merge = imagecreate($width, $height+10*count($source));
        $space = imagecreate($width, 10);

        $dst_x = 0;
        $dst_y = 0;
        for($i=0;$i
具体的思想就是先创建一张空的长图,然后将要合并的图片一张一张放进去(用的是imagecopy()函数),这里我做了一个间隔处理,每两张图片之间间隔了10像素。

到目前为止貌似所有的问题都得到了解决,我们只需要将Java项目打包成jar包,放入JavaBridge所要求的jre环境的ext目录中,就能完成所有的功能了。

但是,当我将所有东西放入项目中运行时,会发生各种“意外”(特别是在Windows开发,然后源码移植到linux上时问题最为严重)。具体有哪些问题,下面我一一列举,并且将之与我的项目结合在一起说明

由于系统不同,所以文件路径的分隔符也不同,在windows下分隔符为"",而Linux下分隔符为"/",于是在我将PDF转为PNG时,这个问题出现了,现在回去看PDF转PNG的源码,会有这样一条代码File file = new File(dest + "" + i + ".png");如果你看懂了我的代码,就会知道问题所在。在Windows下一切运行正常,但是到Linux下PNG文件的文件名和生成路径就会发生变化,这里的""不会被当作路径分隔符了,而是当作文件名的一部分,其实修改起来也很简单:File file = new File(dest + File.separator + i + ".png");

创建另一个线程启动openoffice服务,老是会出现无法连接服务的异常。根据我的各种调试,发现有时候会有这样的情况发生:在程序运行到连接服务的时候,服务并没有开启。按理说开启服务的代码写在连接服务之前,不应该出现这样的问题。我猜测是由于创建了另一个线程来启动服务,而主线程并没有停止,而会继续执行,如果主线程先执行到连接服务的代码,那么就会出现这个错误。虽然是猜测,我觉得八九不离十了,有两种方式解决这个问题:

第一种就是线程等待,让主线程等待,直到另一个线程执行完毕后唤醒主线程;
第二种就是让openoffice服务长期开启,而不需要在程序中启动服务;
由于此时处于开发初期,小组选择先长期开启服务,等后期再改为线程等待策略。那么代码也得做相应的修改,将启动openoffice服务的代码删除即可,但是要记住需要手动启动服务。

如果web网站放在远程服务器上,而且只能用终端进行远程连接没有图形界面时,JavaBridge和openoffice服务就必须放在后台执行了,还好Linux有直接提供将进程转为后台执行的命令,这都不是大问题。

第一个问题和第二个问题需要不断的尝试,才能发现问题,浪费了大部分时间。最后一个问题和Java编程经验有很大关系了。前面提到我们需要将Java项目打包成jar包,而且需要将外部jar包也放入其中。而eclipse在打包的时候不能包含外部jar包(这里打包需要选择JAR File而不能选择Runnable Jar File),我们得想办法将外部jar包塞进去。这里我提供三种方法,但是我只测试过其中一种方法。

方法一:将打包好的jar包用rar打开,将外部jar包直接复制到里面,至于复制到哪里,根据程序中import外部jar包张的类所使用的路径来判断,实在判断不出来就一个目录一个目录尝试。

方法二:将外部jar包解压缩成许多class文件,将class文件复制到目标jar包中,这种方式我通过了测试。例如:将jodconverter的jar包解压缩后,有四个文件夹(com、drafts、org、META-INT),将这四个文件夹复制到目标jar包的顶层目录中。

方法三:如果能找到外部jar包的源代码,可以将源代码直接复制到项目中,跟项目一起打包成jar。这里的源代码指的是.java文件而不是.class文件,这种方式应该百分之百能成,但是一般想要找到源代码很难。

总结一下:

jar文件相当于一个压缩包,可以使用winrar这样的压缩软件打开,并且往里面加入其他文件;

编写程序中如果使用到路径,不要直接用""或"/",使用编程语言中提供的常量来表示分隔符,例如Java中的File.separator,php中的DIRECTORY_SEPARATOR常量;

排查错误的时候,要结合前端和数据库一起进行排查,同时可以通过输入日志判断错误位置,php中提供了error_log()函数输入日志。

jar包的永久下载链接:
jodconverter:https://pan.baidu.com/s/1XwhiVhmlXxVvkPiiIkYi_Q
提取码:1dgj
pdfbox:https://pan.baidu.com/s/19bPBsoJhEv-m0l5ZLlMZbg
提取码:ymnt
JavaBridge:https://pan.baidu.com/s/1XKdC8vSLlmOGIGYRAmSQTA
提取码:k3lb

联系方式(qq):1518542802
若是觉得这篇博客有地方不明白可以加我的qq问我,在下必然知无不言言无不尽。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/31268.html

相关文章

  • php调用Java实现word文档预览

    摘要:在下一切运行正常,但是到下文件的文件名和生成路径就会发生变化,这里的不会被当作路径分隔符了,而是当作文件名的一部分,其实修改起来也很简单创建另一个线程启动服务,老是会出现无法连接服务的异常。 php预览word文档的实现 以及实现过程中遇到的各种坑 在做软件工程的课程设计的时候,我们小组选择做一个资料分享网站,网站最重要的功能当然就是上传文件和下载文件。但是这中间就需要一个比较重要的过...

    HtmlCssJs 评论0 收藏0
  • php调用Java实现word文档预览

    摘要:在下一切运行正常,但是到下文件的文件名和生成路径就会发生变化,这里的不会被当作路径分隔符了,而是当作文件名的一部分,其实修改起来也很简单创建另一个线程启动服务,老是会出现无法连接服务的异常。 php预览word文档的实现 以及实现过程中遇到的各种坑 在做软件工程的课程设计的时候,我们小组选择做一个资料分享网站,网站最重要的功能当然就是上传文件和下载文件。但是这中间就需要一个比较重要的过...

    HitenDev 评论0 收藏0
  • [原]深入对比数据科学工具箱:Python和R Web 编辑器

    摘要:概述工欲善其事必先利其器,如果现在要评选数据科学中最好用的编辑器注意一定是可以通过访问的,和一定是角逐的最大热门,正确使用编辑器可以很大地提升我们的工作效率。 概述 showImg(https://segmentfault.com/img/bVAdol); 工欲善其事必先利其器,如果现在要评选数据科学中最好用的Web 编辑器(注意一定是可以通过Web访问的),RStudio和Jupyt...

    RobinQu 评论0 收藏0
  • PHP制作word简历

    摘要:模板替换的方式制作简历在许多招聘网站都有一个简历下载的功能,如何用实现呢在里面就有一个非常简单的生成一个文档,向文档中插入一些文字。安装创建控制器及方法用于测试,并建立路由。 PHP操作word有一个非常好用的轮子,就是phpword,该轮子可以在github上查找到(PHPOffice/PHPWord)。上面有较为详细的例子和代码,其中里面的源码包含有一些常用的操作例子,包括设置页眉...

    Donne 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<