LibreOffice 实现 Word 转 PDF
一、功能说明
通过 Java 调用 LibreOffice 的命令行工具 soffice,实现无界面(静默)模式 将 Word 文件(.doc/.docx 都支持)高质量转换为 PDF 文件,支持自定义输出文件名、自动创建输出目录,完全适配 Windows/Linux 服务器环境,是生产环境中最常用的办公文件转换方案之一。
二、前置准备(必做)
1. 下载安装 LibreOffice
官网下载:https://www.libreoffice.org/download/download-libreoffice/
- Windows:安装后必须将 LibreOffice 的安装目录
/program/配置到系统环境变量 Path (比如C:\Program Files\LibreOffice\program),配置后重启 IDE/命令行生效 - Linux:直接通过命令安装
yum install libreoffice-headless libreoffice-writer -y(推荐,极简无界面)
2. 核心依赖说明
该方案为纯 Java 原生实现 ,无任何第三方依赖包,无需引入 poi/itext 等 jar 包,通过系统命令调用实现转换,稳定无兼容问题。
三、实现代码
java
import java.io.*;
/**
* LibreOffice 实现 Word(doc/docx) 转 PDF 工具类
* 支持:Windows/Linux 跨平台、无界面静默转换、自定义输出文件名、中文/空格路径
*/
public class LibreOfficeWordToPdfUtil {
// 转换目标格式常量,便于扩展(如修改为jpg/png/txt等)
private static final String TARGET_FORMAT = "pdf";
// LibreOffice 核心命令
private static final String LIBRE_OFFICE_CMD = "soffice";
/**
* Word 转 PDF 核心方法
* @param inputFilePath 输入文件的完整路径(如:D:/test/测试文件.docx)
* @param outputDirPath 输出目录路径(如:D:/pdf/)
* @param outputFileName 输出PDF的文件名(如:转换后的文件.pdf,可省略.pdf后缀,会自动补全)
* @throws IOException 文件不存在/读写失败/转换失败时抛出
* @throws InterruptedException 进程执行被中断时抛出
*/
public static void convertToPdf(String inputFilePath, String outputDirPath, String outputFileName)
throws IOException, InterruptedException {
// 1. 封装文件对象
File inputFile = new File(inputFilePath);
File outputDir = new File(outputDirPath);
// 2. 校验输入文件是否存在
if (!inputFile.exists() || !inputFile.isFile()) {
throw new IOException("【转换失败】输入的Word文件不存在或不是文件:" + inputFilePath);
}
// 3. 校验输入文件是否为 doc/docx 格式
String inputFileName = inputFile.getName();
if (!inputFileName.endsWith(".doc") && !inputFileName.endsWith(".docx")) {
throw new IOException("【转换失败】输入文件不是Word格式,仅支持 .doc / .docx 后缀:" + inputFilePath);
}
// 4. 自动创建输出目录(多级目录也能创建)
if (!outputDir.exists()) {
boolean mkdirsFlag = outputDir.mkdirs();
if (!mkdirsFlag) {
throw new IOException("【转换失败】创建输出目录失败,权限不足:" + outputDirPath);
}
}
// 5. 处理输出文件名,自动补全 .pdf 后缀
String finalOutputFileName = outputFileName.endsWith("." + TARGET_FORMAT)
? outputFileName
: outputFileName + "." + TARGET_FORMAT;
// 6. 构建默认输出文件(LibreOffice转换后的默认文件名,和原Word文件名一致)
String defaultPdfName = inputFileName.substring(0, inputFileName.lastIndexOf(".")) + "." + TARGET_FORMAT;
File defaultOutputFile = new File(outputDir, defaultPdfName);
// 构建最终目标文件(自定义文件名)
File targetOutputFile = new File(outputDir, finalOutputFileName);
// 7. 构建 LibreOffice 转换命令,使用ProcessBuilder避免空格路径问题
ProcessBuilder processBuilder = new ProcessBuilder(
LIBRE_OFFICE_CMD,
"--headless", // 无界面模式,核心!服务器环境必须加,不会弹出窗口
"--nolockcheck", // 关闭文件锁检查,避免文件被占用
"--convert-to", TARGET_FORMAT,
"--outdir", outputDir.getAbsolutePath(),
inputFile.getAbsolutePath()
);
// 合并错误流到标准流,方便统一读取日志
processBuilder.redirectErrorStream(true);
// 8. 执行转换命令并获取进程
Process process = processBuilder.start();
// 读取进程输出日志,避免进程阻塞卡死(核心优化点)
readProcessStream(process.getInputStream());
// 9. 等待进程执行完成,获取退出码(0=成功,非0=失败)
int exitCode = process.waitFor();
if (exitCode != 0) {
throw new IOException("【转换失败】LibreOffice执行命令失败,退出码:" + exitCode + ",请检查文件是否损坏或LibreOffice环境配置");
}
// 10. 校验默认转换后的文件是否生成
if (!defaultOutputFile.exists()) {
throw new IOException("【转换失败】PDF文件转换成功但未生成,未知异常:" + defaultOutputFile.getAbsolutePath());
}
// 11. 重命名文件:默认文件名 -> 自定义文件名
renameOutputFile(defaultOutputFile, targetOutputFile);
// 12. 释放资源
process.getInputStream().close();
process.destroy();
System.out.println("【转换成功】PDF文件生成路径:" + targetOutputFile.getAbsolutePath());
}
/**
* 读取进程的输出流,避免进程阻塞
*/
private static void readProcessStream(InputStream inputStream) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = br.readLine()) != null) {
// 可根据需要打印日志,排查转换问题
// System.out.println("转换日志:" + line);
}
br.close();
}
/**
* 重命名文件,处理目标文件已存在的情况
*/
private static void renameOutputFile(File sourceFile, File targetFile) throws IOException {
// 如果目标文件已存在,先删除(删除前判断是否可写,避免权限问题)
if (targetFile.exists()) {
boolean deleteFlag = targetFile.delete();
if (!deleteFlag) {
throw new IOException("【重命名失败】目标文件已存在且无法删除:" + targetFile.getAbsolutePath());
}
}
// 执行重命名
boolean renameFlag = sourceFile.renameTo(targetFile);
if (!renameFlag) {
throw new IOException("【重命名失败】无法将文件从 " + sourceFile.getAbsolutePath() + " 重命名为 " + targetFile.getAbsolutePath());
}
}
// ========== 测试调用示例 ==========
public static void main(String[] args) {
try {
// 测试参数:输入文件路径、输出目录、自定义PDF文件名
String inputPath = "D:/test/我的测试文档.docx";
String outputDir = "D:/test/pdf输出";
String outputFileName = "测试文档_转换版";
convertToPdf(inputPath, outputDir, outputFileName);
} catch (Exception e) {
e.printStackTrace();
}
}
}
四、核心API说明 & 关键参数解释
1. 核心命令参数详解
bash
soffice --headless --nolockcheck --convert-to pdf --outdir 输出目录 输入文件路径
--headless:无界面模式,最核心参数,服务器/生产环境必须添加,不会弹出LibreOffice窗口,静默执行--nolockcheck:关闭文件锁检查,避免文件被占用导致转换失败--convert-to pdf:指定转换的目标格式为PDF(可替换为jpg/png/txt/odt等,LibreOffice支持超多格式)--outdir:指定转换后的文件输出目录- 最后跟待转换的文件路径
2. 方法入参说明
inputFilePath:必填 ,Word文件的完整绝对路径,例如D:/test/abc.docxoutputDirPath:必填 ,PDF输出的目录路径,例如D:/test/out,目录不存在会自动创建outputFileName:必填 ,PDF的自定义文件名,支持「带后缀」和「不带后缀」两种写法,例如test.pdf/test均可
五、使用注意事项(避坑指南 · 重中之重)
通用注意事项
- 转换后的PDF文件会完美保留原Word的格式、图片、表格、字体样式,比poi+itext的方案转换质量高得多
- 必须保证输出目录有写入权限,输入文件有读取权限,否则会抛出权限异常
- 支持批量转换,循环调用该方法即可,无并发限制
Windows 环境专属
- LibreOffice安装后必须配置环境变量 ,否则会提示
soffice 不是内部或外部命令 - 如果转换中文文件乱码,需要在Windows上安装对应的中文字体(宋体、微软雅黑等)
Linux 服务器环境专属(生产环境99%是Linux,必看)
- 安装命令推荐:
yum install libreoffice-headless libreoffice-writer libreoffice-langpack-zh-CN -y,安装中文语言包避免中文乱码 - Linux下无需配置环境变量 ,
soffice命令全局可用 - 解决Linux中文乱码核心方案:把Windows的中文字体(比如
simsun.ttc宋体)上传到Linux的/usr/share/fonts目录,执行fc-cache -fv更新字体缓存 - Linux下执行时,确保运行Java程序的用户有执行
soffice命令的权限
六、常见问题排查
问题1:转换失败,退出码 exitCode != 0
- 原因1:LibreOffice环境未配置好,执行
soffice --version命令测试是否能正常输出版本信息 - 原因2:输入文件损坏或不是标准的doc/docx文件
- 原因3:输出目录权限不足,无法写入文件
- 原因4:文件被其他程序占用(比如Word打开未关闭)
问题2:转换后的PDF中文显示为方框/乱码
- 解决方案:系统缺少中文字体,Windows安装中文字体,Linux上传中文字体并更新缓存(上文已说明)
问题3:进程卡死,无响应,不抛出异常
- 原因:没有读取进程的输出流,导致缓冲区满了,进程阻塞
- 解决方案:代码中已经通过
readProcessStream方法处理,无需额外修改
问题4:重命名文件失败
- 原因:目标文件已存在且被占用,或权限不足
- 解决方案:代码中会先删除已存在的目标文件,再执行重命名,确保无冲突
七、扩展能力
LibreOffice的转换能力极强,该工具类仅需修改常量 TARGET_FORMAT,即可实现:
- Word 转 图片(
jpg/png) - Word 转 纯文本(
txt) - Word 转 电子书(
epub) - Excel/PowerPoint 转 PDF 等
总结
该方案是生产环境最优的办公文件转换方案,相比POI+IText的纯Java代码方案,优势在于:
- 无需处理复杂的Office格式兼容问题,转换质量极高
- 无第三方依赖,代码简洁,维护成本低
- 支持所有Office文件格式,扩展能力强
- 跨平台兼容,Windows/Linux无缝切换