⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 blog.csdn.net/qq_46548855 「Knight_AL」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

在日常项目开发中,我们经常遇到这样的需求:

用户上传 Word( .docx)文件,希望后台自动生成 PDF,用于下载、归档或在线预览。

网上方案很多——有收费的 Aspose、有重量级的 LibreOffice,也有轻量的 docx4j。

本文将介绍一个完全开源、部署简单、纯 Java 的实现方案:

使用 docx4j 在 Spring Boot 中实现 .docx → .pdf 转换。

一、方案选型对比

方案 是否开源 外部依赖 样式保真度 部署复杂度 备注
Apache POI + iText ✅ 无 中 ⭐ 对复杂格式支持差
docx4j ✅ 无 高 ⭐⭐ 推荐,纯 Java
LibreOffice + JODConverter ✅ 需安装 LibreOffice 很高 ⭐⭐⭐ 部署复杂
Aspose.Words ❌ 无 最高 ⭐ 收费,商业许可

选择理由:

  • docx4j 是纯 Java 实现,无需安装 Office 或 LibreOffice;
  • 开源(Apache 2.0 License),免费可商用;
  • 转换质量好,能保留图片、表格、页眉页脚;
  • 容易集成进 Spring Boot。

二、添加 Maven 依赖

在 pom.xml 中加入以下依赖:

<dependencies>
<dependency>
<groupId>org.docx4j</groupId>
<artifactId>docx4j-core</artifactId>
<version>11.4.8</version>
</dependency>

<dependency>
<groupId>org.docx4j</groupId>
<artifactId>docx4j-JAXB-ReferenceImpl</artifactId>
<version>11.4.8</version>
</dependency>

<dependency>
<groupId>org.docx4j</groupId>
<artifactId>docx4j-export-fo</artifactId>
<version>11.4.8</version>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.9</version>
</dependency>

</dependencies>

三、核心工具类:DocxToPdfUtil

在 utils 包下创建 DocxToPdfUtil.java:

package com.donglin.utils;

import org.docx4j.Docx4J;
import org.docx4j.fonts.IdentityPlusMapper;
import org.docx4j.fonts.Mapper;
import org.docx4j.fonts.PhysicalFont;
import org.docx4j.fonts.PhysicalFonts;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;

import java.io.File;
import java.io.FileOutputStream;

public class DocxToPdfUtil {

/**
* 将 docx 文件转换为 PDF
*
* @param docxPath 输入文件路径
* @param pdfPath 输出文件路径
*/
public static void convert(String docxPath, String pdfPath) {
try {
// 1. 加载 Word 文档
WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(new File(docxPath));

// 2. 配置字体映射(防止中文乱码)
Mapper fontMapper = new IdentityPlusMapper();
PhysicalFonts.discoverPhysicalFonts();

PhysicalFont simsun = PhysicalFonts.get("SimSun");
if (simsun != null) {
fontMapper.put("SimSun", simsun);
// 常用中文字体映射表
fontMapper.put("隶书", PhysicalFonts.get("LiSu"));
fontMapper.put("宋体", PhysicalFonts.get("SimSun"));
fontMapper.put("微软雅黑", PhysicalFonts.get("Microsoft YaHei"));
fontMapper.put("黑体", PhysicalFonts.get("SimHei"));
fontMapper.put("楷体", PhysicalFonts.get("KaiTi"));
fontMapper.put("新宋体", PhysicalFonts.get("NSimSun"));
fontMapper.put("华文行楷", PhysicalFonts.get("STXingkai"));
fontMapper.put("华文仿宋", PhysicalFonts.get("STFangsong"));
fontMapper.put("仿宋", PhysicalFonts.get("FangSong"));
fontMapper.put("幼圆", PhysicalFonts.get("YouYuan"));
fontMapper.put("华文宋体", PhysicalFonts.get("STSong"));
fontMapper.put("华文中宋", PhysicalFonts.get("STZhongsong"));
fontMapper.put("等线", PhysicalFonts.get("SimSun"));
fontMapper.put("等线 Light", PhysicalFonts.get("SimSun"));
fontMapper.put("华文琥珀", PhysicalFonts.get("STHupo"));
fontMapper.put("华文隶书", PhysicalFonts.get("STLiti"));
fontMapper.put("华文新魏", PhysicalFonts.get("STXinwei"));
fontMapper.put("华文彩云", PhysicalFonts.get("STCaiyun"));
fontMapper.put("方正姚体", PhysicalFonts.get("FZYaoti"));
fontMapper.put("方正舒体", PhysicalFonts.get("FZShuTi"));
fontMapper.put("华文细黑", PhysicalFonts.get("STXihei"));
fontMapper.put("宋体扩展", PhysicalFonts.get("simsun-extB"));
fontMapper.put("仿宋_GB2312", PhysicalFonts.get("FangSong_GB2312"));
fontMapper.put("新細明體", PhysicalFonts.get("SimSun"));

// ⚙️ 修复 “宋体(正文)/宋体(标题)” 乱码
PhysicalFonts.put("PMingLiU", PhysicalFonts.get("SimSun"));
PhysicalFonts.put("新細明體", PhysicalFonts.get("SimSun"));
wordMLPackage.setFontMapper(fontMapper);
}

// 3. 创建输出流并执行转换
try (FileOutputStream os = new FileOutputStream(pdfPath)) {
Docx4J.toPDF(wordMLPackage, os);
}

System.out.println("✅ PDF 生成成功:" + pdfPath);
} catch (Exception e) {
System.err.println("❌ 转换失败:" + e.getMessage());
}
}
}

四、Controller 示例

在 controller 包中创建一个上传接口:

import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;

@RestController
@RequestMapping("/convert")
public class FileController {

@GetMapping("/convertToPdf")
public void convertToPdf(@RequestParam String filePath, HttpServletResponse response) throws Exception {
// 1、 检查文件是否存在
File inputFile = new File(filePath);
if (!inputFile.exists()) {
throw new RuntimeException("文件不存在: " + filePath);
}

// 2、 定义输出路径(临时文件)
String pdfPath = filePath.replace(".docx", ".pdf");

// 3、 调用转换工具
DocxToPdfUtil.convert(filePath, pdfPath);

// 4、 设置响应头并输出 PDF 文件
response.setContentType("application/pdf");
response.setHeader("Content-Disposition", "attachment; filename=" + new File(pdfPath).getName());
try (FileInputStream fis = new FileInputStream(pdfPath);
OutputStream os = response.getOutputStream()) {
fis.transferTo(os);
os.flush();
}

// 可选:删除临时 PDF 文件
new File(pdfPath).delete();
}
}

使用 Postman 发送请求:

> GET http://localhost:8080/convertToPdf?filePath=filePath=E:/ai/report.docx
>

选择一个 .docx 文件上传,即可生成同名 .pdf 文件。

五、Windows解决中文乱码问题

增加字体的类别

fontMapper.put("SimSun", simsun);
// 常用中文字体映射表
fontMapper.put("隶书", PhysicalFonts.get("LiSu"));
fontMapper.put("宋体", PhysicalFonts.get("SimSun"));
fontMapper.put("微软雅黑", PhysicalFonts.get("Microsoft YaHei"));
fontMapper.put("黑体", PhysicalFonts.get("SimHei"));
fontMapper.put("楷体", PhysicalFonts.get("KaiTi"));
fontMapper.put("新宋体", PhysicalFonts.get("NSimSun"));
fontMapper.put("华文行楷", PhysicalFonts.get("STXingkai"));
fontMapper.put("华文仿宋", PhysicalFonts.get("STFangsong"));
fontMapper.put("仿宋", PhysicalFonts.get("FangSong"));
fontMapper.put("幼圆", PhysicalFonts.get("YouYuan"));
fontMapper.put("华文宋体", PhysicalFonts.get("STSong"));
fontMapper.put("华文中宋", PhysicalFonts.get("STZhongsong"));
fontMapper.put("等线", PhysicalFonts.get("SimSun"));
fontMapper.put("等线 Light", PhysicalFonts.get("SimSun"));
fontMapper.put("华文琥珀", PhysicalFonts.get("STHupo"));
fontMapper.put("华文隶书", PhysicalFonts.get("STLiti"));
fontMapper.put("华文新魏", PhysicalFonts.get("STXinwei"));
fontMapper.put("华文彩云", PhysicalFonts.get("STCaiyun"));
fontMapper.put("方正姚体", PhysicalFonts.get("FZYaoti"));
fontMapper.put("方正舒体", PhysicalFonts.get("FZShuTi"));
fontMapper.put("华文细黑", PhysicalFonts.get("STXihei"));
fontMapper.put("宋体扩展", PhysicalFonts.get("simsun-extB"));
fontMapper.put("仿宋_GB2312", PhysicalFonts.get("FangSong_GB2312"));
fontMapper.put("新細明體", PhysicalFonts.get("SimSun"));

// ⚙️ 修复 “宋体(正文)/宋体(标题)” 乱码
PhysicalFonts.put("PMingLiU", PhysicalFonts.get("SimSun"));
PhysicalFonts.put("新細明體", PhysicalFonts.get("SimSun"));
wordMLPackage.setFontMapper(fontMapper);

六、Linux解决中文乱码问题

在 Linux 环境中安装 Windows 字体

新建字体文件夹

sudo mkdir -p /usr/share/fonts/win_font

拷贝 Windows 字体文件

将 Windows 10 系统中路径为 C:\Windows\Fonts 的字体文件拷贝到 Linux 的 /usr/share/fonts/win_font 目录中。

加载字体文件

进入字体目录并执行以下命令:

cd /usr/share/fonts/win_font
sudo mkfontscale # 生成字体缩放文件
sudo mkfontdir # 生成字体目录索引
sudo fc-cache -fv # 刷新字体缓存

查看字体安装情况

执行以下命令查看中文字体是否成功加载:

fc-list :lang=zh

七、总结

  • 使用开源库 docx4j;
  • 无需安装 Office 或 LibreOffice;
  • 保留常见样式、图片、表格;
  • 性能高、部署轻量。
文章目录
  1. 1. 一、方案选型对比
  2. 2. 二、添加 Maven 依赖
  3. 3. 三、核心工具类:DocxToPdfUtil
  4. 4. 四、Controller 示例
  5. 5. 五、Windows解决中文乱码问题
  6. 6. 六、Linux解决中文乱码问题
    1. 6.0.0.1. 新建字体文件夹
    2. 6.0.0.2. 拷贝 Windows 字体文件
    3. 6.0.0.3. 加载字体文件
    4. 6.0.0.4. 查看字体安装情况
  • 7. 七、总结