文章目录

  • 引言
  • jacob
  • 环境搭建
  • 代码示例
  • 自定义线程池方式
  • 非自定义线程池
  • 可选择WPS还是office方式
  • 测试
  • documents4j
  • 环境搭建
  • 代码示例
  • 构建工具类
  • 测试
  • 总结


引言

如果你的项目是需要部署在linux环境下,这篇文章可以只看引言部分,如果可以部署在windows环境下,你可以继续阅读

在最近的需求中,有需要实现word转pdf的功能,在此记录学习的过程。经过一段时间的查阅资料,发现大体有以下几种选择去实现该需求,我没有尝试成功的就不上代码了,跟大家在引言部分说明下原因。

  • jacob

免费,效果好,缺点是必须windows环境,装wps或office

  • documents4j

免费,效果好,缺点是必须windos环境,装office

  • asponse

该方式需要收费,如果使用免费版本会有水印的存在,而且网上流传的license方法基本已经失效,想要使用,自己想办法

  • Spire.Doc

该方式需要收费,免费版本仅仅支持转换前3页,效果好

  • poi

该方式不需要收费,但我使用该方式转换的文件会出现排版混乱的问题

  • XdocReport

该方式不需要收费,但我使用该方式转换的文件会出现排版混乱的问题

  • docx4j

该方式不需要收费,但我使用该方式转换的文件会出现排版混乱的问题

如果你只是在完成个人学习项目的需求,部署环境为linux,我推荐可以采用asponseSpire.Doc。如果你有足够的精力去精研,可以去尝试poi的方式。

由于我这边项目部署是windows环境,所以这里选择 documents4j ,因为该方式相比于 jacob 不需要去修改 jdk 的原生环境,在下面我将给大家具体去实现并介绍我觉得还可以的两种方式documents4jjacob

jacob

环境搭建

要求:

  • windows环境
  • 有WPS或Office(选择数据源)

优点:

  • 免费
  • 转换效果好

缺点:

  • 转换速度较慢
  • 必须windows环境
  • 需要改变jdk原环境
  1. 进入jacob.jar下载页面
  2. 下载zip包
  3. 解压压缩包后,可以看见其中有以下文件
  4. 选择自己电脑合适版本的dll文件,放入jdk中jre的bin目录中,即C:\Program Files\Java\jdk1.8.0_311\jre\bin
  5. 将需要使用的jar包在项目中引入即可(如果需要使用maven方式引入,请自行去搜索如何引入)

代码示例

经过从网上搜查的资料,我将其整理成了3个方式的工具类,都可以直接使用

  • 自定义线程池方式:该方式可以减少转换所需的时间,只是在类初始化的时候时间有点慢
  • 非自定义线程池:这个方式我个人来感觉是最满足官方要求的,使用自定义线程池的方式我认为有点不符合规范,但目前还发现问题
  • 可选择wps还是office方式:根据当前环境是安装的wps还是office来选择ActiveXComponent

自定义线程池方式

初始化20多秒,后面每次都是3秒左右

import com.jacob.activeX.ActiveXComponent;
import com.jacob.com.Dispatch;
import com.jacob.com.Variant;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.io.File;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * <p>
 *
 * </p>
 *
 * @author:雷子杰
 * @date:2022/9/6
 */
@Slf4j
@Component
//Word.Application
public class TestUtil {
    //word 转换为 pdf 的格式宏,值为 17
    private static final int WORD_FORMAT_PDF = 17;
    // 线程池队列
    private static BlockingQueue<Dispatch> dispatchPool = new LinkedBlockingQueue<>();
    // 线程池大小
    private static int MAX_DISPATCH_SIZE = 10;

    static {
        long start = System.currentTimeMillis();

        for (int i = 0; i < MAX_DISPATCH_SIZE; i++) {
            // 这里需要根据当前环境安装的是 MicroSoft Office还是WPS来选择
            // 如果安装的是WPS,则需要使用 KWPS.Application
            // 如果安装的是微软的 Office,需要使用 Word.Application
            ActiveXComponent axc = new ActiveXComponent("Word.Application");
            axc.setProperty("Visible", false);
            axc.setProperty("AutomationSecurity", new Variant(3));
            Dispatch dispatch = axc.getProperty("Documents").toDispatch();
            dispatchPool.add(dispatch);
        }

        long end = System.currentTimeMillis();
        log.debug("初始化线程池队列时间:"+(end-start));
    }

    /**
     * Word 转 PDF
     * @param wordFile Word文件全路径
     * @param pdfFile Pdf全路径
     */
    public static void wordToPdf(String wordFile, String pdfFile) {
        log.debug("Word转PDF开始启动...");
        long start = System.currentTimeMillis();

        Dispatch dispatch = null;

        try {
            File outFile = new File(pdfFile);

            // 如果目标文件存在,则先删除
            if (outFile.exists()) {
                outFile.delete();
            }

            //获取阻塞队列中disoatch
            dispatch =getDispatch();

            Dispatch document = Dispatch.call(dispatch, "Open", wordFile, false, true).toDispatch();
            //Dispatch.call(document, "ExportAsFixedFormat", pdfFile, WORD_FORMAT_PDF);

            Dispatch.call(document, "SaveAs", pdfFile, WORD_FORMAT_PDF);
            Dispatch.call(document, "Close", false);

            long end = System.currentTimeMillis();
            log.info("word转pdf时间:" + (end - start) + "ms");

        } catch (Exception e) {
            log.error("Word转PDF出错:" + e.getMessage());
            return;
        } finally {

            if (dispatch != null) {
                //将dispathch添加回去阻塞队列中
                relaseDispatch(dispatch);
            }

        }
    }

    @SneakyThrows
    private static Dispatch getDispatch() {
        return dispatchPool.take();
    }

    @SneakyThrows
    private static void relaseDispatch(Dispatch dispatch) {
        dispatchPool.add(dispatch);
    }

}

非自定义线程池

均速在9秒左右

import com.jacob.activeX.ActiveXComponent;
import com.jacob.com.ComThread;
import com.jacob.com.Dispatch;
import com.jacob.com.Variant;
import lombok.extern.slf4j.Slf4j;

import java.io.File;

/**
 * <p>
 *
 * </p>
 *
 * @author:雷子杰
 * @date:2022/9/6
 */
@Slf4j
public class Test2Util {

    public static void convertWordToPdf(String wordFile,String pdfFile){
        File file = new File(wordFile);
        if(file.exists()){
            if (!file.isDirectory()) {   //判断word文件存不存在

                ActiveXComponent app = null;
                log.debug("============开始转换============");
                // 开始时间
                long start = System.currentTimeMillis();
                try {
                    //新增优化代码==============>初始化com的线程
                    // ComThread.InitSTA();   仅允许线程池里面的一个线程执行,其他线程都被锁住
                    ComThread.InitMTA(true);  //允许同时有多个WORD进程运行
                    // 打开word
                    app = new ActiveXComponent("Word.Application");
                    // 设置word不可见
                    app.setProperty("Visible", false);
                    // 获得所有打开的文档
                    Dispatch documents = app.getProperty("Documents").toDispatch();
                    log.debug("============打开文件: " + wordFile);
                    // 打开文档
                    Dispatch document = Dispatch.call(documents, "Open", wordFile, false, true).toDispatch();
                    // 判断文件是否存在
                    File target = new File(pdfFile);
                    if (target.exists()) {
                        target.delete();
                    }

                    // 另存为,将文档保存为pdf,其中word保存为pdf的格式宏的值是17
                    Dispatch.call(document, "SaveAs", pdfFile, 17);
                    // 关闭文档
                    Dispatch.call(document, "Close", false);
                    // 结束时间

                    long end = System.currentTimeMillis();
                    System.out.println("word转换pdf时间:" + (end - start) + "ms");

                }catch(Exception e) {
                    log.error("pdf转换发生异常convertWordToPdf:"+e.getLocalizedMessage());
                    throw new RuntimeException("pdf转换失败!请联系技术人员。");
                }finally {

                    // 关闭office
                    if (app != null) {
                        long start2 = System.currentTimeMillis();

                        app.invoke("Quit", new Variant[] {});

                        long end2 = System.currentTimeMillis();
                        log.debug("关闭office的时间:"+(end2-start2));

                    }
                    long start3 = System.currentTimeMillis();
                    //新增优化代码==============>关闭com的线程
                    ComThread.Release();
                    long end3 = System.currentTimeMillis();
                    log.debug("关闭com线程的时间:"+(end3-start3));
                }
            }
        }
    }
}

可选择WPS还是office方式

import com.jacob.activeX.ActiveXComponent;
import com.jacob.com.ComThread;
import com.jacob.com.Dispatch;
import com.jacob.com.Variant;
import lombok.SneakyThrows;

import java.io.File;
import java.nio.file.Files;
import java.util.Objects;

/**
 * <p>
 *
 * </p>
 *
 * @author:雷子杰
 * @date:2022/9/6
 */
public class WordToPdfUtils {
    /** word 转换为 pdf 的格式宏,值为 17 */
    private static final int WORD_FORMAT_PDF = 17;
    private static final String MS_OFFICE_APPLICATION = "Word.Application";
    private static final String WPS_OFFICE_APPLICATION = "KWPS.Application";

    /**
     * 微软Office Word转PDF
     * 如果无法转换,可能需要下载 SaveAsPDFandXPS.exe 插件并安装
     * @param wordFile Word文件
     * @param pdfFile Pdf文件
     */
    public static void msOfficeToPdf(String wordFile, String pdfFile) {
        wordToPdf(wordFile, pdfFile, MS_OFFICE_APPLICATION);
    }

    /**
     * WPS Office Word转PDF
     * @param wordFile Word文件
     * @param pdfFile Pdf文件
     */
    public static void wpsOfficeToPdf(String wordFile, String pdfFile) {
        wordToPdf(wordFile, pdfFile, WPS_OFFICE_APPLICATION);
    }

    /**
     * Word 转 PDF
     * @param wordFile Word文件
     * @param pdfFile Pdf文件
     * @param application Office 应用
     */
    private static void wordToPdf(String wordFile, String pdfFile, String application) {
        Objects.requireNonNull(wordFile);
        Objects.requireNonNull(pdfFile);
        Objects.requireNonNull(application);

        ActiveXComponent app = null;
        Dispatch document = null;
        try {
            File outFile = new File(pdfFile);
            // 如果目标路径不存在, 则新建该路径,否则会报错
            if (!outFile.getParentFile().exists()) {
                Files.createDirectories(outFile.getParentFile().toPath());
            }

            // 如果目标文件存在,则先删除
            if (outFile.exists()) {
                outFile.delete();
            }

            // 这里需要根据当前环境安装的是 MicroSoft Office还是WPS来选择
            // 如果安装的是WPS,则需要使用 KWPS.Application
            // 如果安装的是微软的 Office,需要使用 Word.Application
            app = new ActiveXComponent(application);
            app.setProperty("Visible", new Variant(false));
            app.setProperty("AutomationSecurity", new Variant(3));

            Dispatch documents = app.getProperty("Documents").toDispatch();
            document = Dispatch.call(documents, "Open", wordFile, false, true).toDispatch();

            Dispatch.call(document, "ExportAsFixedFormat", pdfFile, WORD_FORMAT_PDF);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (document != null) {
                Dispatch.call(document, "Close", false);
            }

            if (app != null) {
                app.invoke("Quit", 0);
            }

            ComThread.Release();
        }
    }

}

测试

测试我这里就不展示了,就大概说一下逻辑

  1. 判断文件类型,是否是word文档
  2. 判断文件是否存在
  3. word转换为pdf(转换出来的路径默认是word文件的资源路径)

documents4j

环境搭建

要求:

  • windows环境
  • 有Office

优点:

  • 免费
  • 转换效果好

缺点:

  • 必须windows环境
  • 只支持docx格式

引入maven依赖

<dependency>
    <groupId>com.documents4j</groupId>
    <artifactId>documents4j-local</artifactId>
    <version>1.1.1</version>
</dependency>

<dependency>
    <groupId>com.documents4j</groupId>
    <artifactId>documents4j-transformer-msoffice-word</artifactId>
    <version>1.1.1</version>
</dependency>

代码示例

执行该段程序,即可

import com.documents4j.api.DocumentType;
import com.documents4j.api.IConverter;
import com.documents4j.job.LocalConverter;

import java.io.File;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 *
 * </p>
 *
 * @author:雷子杰
 * @date:2022/9/8
 */
public class wordToPdf {
    public static void main(String[] args) {
        //示例:File wordFile = "D:\\SimpleSolution\\Data\\Welcome to Word.docx";
        File wordFile = new File( "需要转换的word文件全路径");
        File target = new File( "转换后pdf文件路径" );

        IConverter converter = LocalConverter.builder()
                .baseFolder(new File("D:\\SimpleSolution\\Data"))
                .workerPool(20, 25, 2, TimeUnit.SECONDS)
                .processTimeout(30, TimeUnit.SECONDS)
                .build();

        try {
            converter
                    .convert(wordFile).as(DocumentType.MS_WORD)
                    .to(target).as(DocumentType.PDF)
                    .execute();
        }finally {
            converter.shutDown();
        }

    }
}

构建工具类

这个是已经封装好的工具类,直接拿去用即可

import com.documents4j.api.DocumentType;
import com.documents4j.api.IConverter;
import com.documents4j.job.LocalConverter;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * word转换工具类
 * </p>
 *
 * @author:雷子杰
 * @date:2022/9/8
 */
@Slf4j
public class WordConvertUtil {

    /**
     * 使用document4j
     * word转换为pdf
     *
     * @param wordPath word全路径
     * @param pdfPath 转换成的pdf全路径
     */
    public static void wordToPdf(String wordPath,String pdfPath){
        /*
        根据传进来的全路径,转化为文件对象
         */
        File source = new File(wordPath);
        File target = new File(pdfPath);

        /*
        Local converter
         */
        IConverter converter = LocalConverter.builder()
                //临时文件路径,word文件的目录,之后会更换一个固定的
                .baseFolder(new File(source.getParent()))
                .workerPool(20, 25, 2, TimeUnit.SECONDS)
                .processTimeout(30, TimeUnit.SECONDS)
                .build();

        /*
        The API
         */
        try {
            converter
                    .convert(source).as(DocumentType.MS_WORD) //来源文件(可为流形式)、文件类型
                    .to(target).as(DocumentType.PDF) //转换成的文件(可为流形式),文件类型
                    .execute();

        }finally {
            //关闭转换器
            converter.shutDown();
        }


    }

}

测试

这里就不展示了

总结

目前只是解决了在windows下如何将word转换为pdf的功能,如何在linux中去实现,目前还未解决。