最近实现了一个将docx转成PDF的功能,这里来记录一下实现过程
我是参考这篇文章Java将Word转换成PDF的常用用法_java_脚本之家
实现步骤基本上是按照上面文档中描述的内容,把大象装冰箱一共就三步
1、导入依赖
XML
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>wordToPDF</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.3.5</version>
<scope>compile</scope>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.6</version>
</dependency>
<!--word转换为PDF文档-->
<dependency>
<groupId>com.documents4j</groupId>
<artifactId>documents4j-local</artifactId>
<version>1.0.3</version>
</dependency>
<dependency>
<groupId>com.documents4j</groupId>
<artifactId>documents4j-transformer-msoffice-word</artifactId>
<version>1.0.3</version>
</dependency>
</dependencies>
</project>
2、增加日志的配置文件(这一步可以忽略)
application.yml
XML
logging:
config: classpath:log4j2.xml
log4j2.xml
XML
<?xml version="1.0" encoding="UTF-8"?>
<configuration status="INFO" monitorInterval="30">
<Properties>
<property name="ALL_LOG_FILE_NAME">
log/
</property>
<!-- 输出日志的格式 -->
<property name="PATTERN">[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%M-%t] [%-5level] %logger{36}:%L - %msg%n</property>
</Properties>
<Appenders>
<!--这个输出控制台的配置 -->
<Console name="Console" target="SYSTEM_OUT">
<!-- 控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch) -->
<ThresholdFilter level="trace" onMatch="ACCEPT" onMismatch="DENY"/>
<PatternLayout pattern="${PATTERN}"/>
</Console>
<RollingFile name="RollingFileAll"
fileName="${ALL_LOG_FILE_NAME}/all.log"
filePattern="${ALL_LOG_FILE_NAME}/all-%d{yyyy-MM-dd}-%i.log">
<Filters>
<!--设置只输出级别为info的日志-->
<ThresholdFilter level="all"/>
</Filters>
<!--文件输出格式-->
<PatternLayout pattern="${PATTERN}"/>
<Policies>
<!--每天一个文件-->
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
<!--单个文件的大小-->
<SizeBasedTriggeringPolicy size="100 MB"/>
</Policies>
<!--设置保留的文件数据-->
<DefaultRolloverStrategy max="12">
<!--删除的规则-->
<Delete basePath="${ALL_LOG_FILE_NAME}" maxDepth="2">
<!--保存文件名-->
<IfFileName glob="all-*.log"/>
<!--保留天数-->
<IfLastModified age="30d"/>
</Delete>
</DefaultRolloverStrategy>
</RollingFile>
</Appenders>
<!--然后定义logger,只有定义了logger并引入的appender,appender才会生效-->
<loggers>
<!--过滤掉spring和mybatis的一些无用的DEBUG信息-->
<logger name="org.springframework" level="warn"/>
<logger name="org.mybatis" level="warn"/>
<logger name="org.apache.kafka" level="warn"/>
<root level="info">
<appender-ref ref="Console"/>
<appender-ref ref="RollingFileAll"/>
</root>
</loggers>
</configuration>
3、核心代码
java
package com.youyou;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import com.documents4j.api.DocumentType;
import com.documents4j.api.IConversionJobWithPriorityUnspecified;
import com.documents4j.api.IConversionJobWithSourceSpecified;
import com.documents4j.api.IConversionJobWithSourceUnspecified;
import com.documents4j.api.IConversionJobWithTargetUnspecified;
import com.documents4j.api.IConverter;
import com.documents4j.job.LocalConverter;
import lombok.extern.slf4j.Slf4j;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
@Slf4j
public class Documents4jUtil {
public static void main(String[] args) throws FileNotFoundException {
File file = FileUtil.touch("D:\\Desktop\\SIU测试工装使用方法2.pdf");
try (InputStream inputStream = IoUtil.toStream(FileUtil.file("D:\\Desktop\\SIU测试工装使用方法.docx"));
OutputStream outputStream = new FileOutputStream(file)) {
convertWordToPdf(inputStream, outputStream);
System.out.println("完成");
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* word转pdf
*/
public static void convertWordToPdf(InputStream stream, OutputStream sourceOutput) {
String os = System.getProperty("os.name").toLowerCase();
log.info("convertWordToPdf 当前操作系统:{}", os);
if (os.contains("win")) {
// Windows操作系统
windowsWordToPdf(stream, sourceOutput);
} else if (os.contains("nix") || os.contains("nux") || os.contains("mac")) {
// Unix/Linux/Mac操作系统
linuxWordToPdf(stream, sourceOutput);
} else {
// 未知操作系统
throw new RuntimeException("不支持当前操作系统转换文档。");
}
}
/**
* 通过documents4j 实现word转pdf -- Windows 环境 需要有 Microsoft Office 服务
*/
public static void windowsWordToPdf(InputStream stream, OutputStream sourceOutput) {
try {
IConverter converter = LocalConverter.builder().build();
IConversionJobWithSourceUnspecified convert = converter.convert(stream);
IConversionJobWithSourceSpecified sourceSpecified = convert.as(DocumentType.DOCX);
IConversionJobWithTargetUnspecified to = sourceSpecified.to(sourceOutput);
IConversionJobWithPriorityUnspecified priorityUnspecified = to.as(DocumentType.PDF);
boolean execute = priorityUnspecified.execute();
log.info("转换结果:{}", execute);
converter.shutDown();
} catch (Exception e) {
log.error("winWordToPdf windows环境word转换为pdf时出现异常:", e);
}
}
/**
* 通过libreoffice 实现word转pdf -- linux 环境 需要有 libreoffice 服务
*/
public static void linuxWordToPdf(InputStream stream, OutputStream sourceOutput) {
// 创建临时文件
File tempFile = createTempFileFromInputStream(stream);
// 构建LibreOffice的命令行工具命令
String command = "libreoffice6.4 --headless --invisible --convert-to pdf " + tempFile.getAbsolutePath() + " --outdir " + tempFile.getParent();
// 执行转换命令
try {
if (!executeLinuxCmd(command)) {
throw new IOException("转换失败");
}
readPdfFileToByteArrayOutputStream(tempFile, sourceOutput);
} catch (Exception e) {
log.error("ConvertWordToPdf: Linux环境word转换为pdf时出现异常:" + e + tempFile.getPath());
// 清理临时文件
tempFile.delete();
} finally {
File pdfFile = new File(tempFile.getParent(), tempFile.getName().replace(".docx", ".pdf"));
//清理转换后的pdf文件
pdfFile.delete();
// 清理临时文件,无论是否成功转换
tempFile.delete();
}
}
/**
* 执行命令行
*
* @param cmd 命令行
* @return
* @throws IOException
*/
private static boolean executeLinuxCmd(String cmd) throws IOException {
Process process = Runtime.getRuntime().exec(cmd);
try {
process.waitFor();
} catch (InterruptedException e) {
log.error("executeLinuxCmd 执行Linux命令异常:", e);
Thread.currentThread().interrupt();
return false;
}
return true;
}
/**
* 创建临时文件
*/
private static File createTempFileFromInputStream(InputStream inputStream) {
try {
File tempFile = File.createTempFile("temp_word", ".docx");
Files.copy(inputStream, tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
return tempFile;
} catch (IOException e) {
log.error("创建临时文件失败:", e);
throw new RuntimeException("创建临时文件失败", e);
}
}
/**
* 读取pdf文件
*/
private static void readPdfFileToByteArrayOutputStream(File tempFile, OutputStream sourceOutput) {
try {
Path outputFile = Paths.get(tempFile.getParent(), tempFile.getName().replace(".docx", ".pdf"));
Files.copy(outputFile, sourceOutput);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
同时我也把相关代码上传到了gitee上地址如下: