基于 Spring Boot + JODConverter 实现文档在线转换为 PDF 功能

基于 Spring Boot + JODConverter 实现文档在线转换为 PDF 功能

适用场景 :企业办公系统、OA、知识库、文档管理平台等需要将 Word/Excel/PPT 等格式转为 PDF 的 Web 应用
技术栈:Spring Boot 3.x + JODConverter + LibreOffice + Hutool + OSS


一、背景与需求

在很多企业级应用中,用户上传的 .docx、.xlsx、.pptx 等 Office 文档,往往需要统一转换为 PDF 格式进行预览、归档或打印。原因包括:

  • PDF 具有跨平台一致性;
  • 防止内容被随意编辑;
  • 便于前端使用 PDF.js 等库进行安全预览。

而市面上成熟的解决方案中,LibreOffice + JODConverter 是一个开源、稳定且支持多种格式的组合,非常适合集成到 Spring Boot 项目中。

本文将带你从零搭建一个 RESTful 文档转 PDF 接口,并附上完整的生产级代码。


二、技术选型说明

组件 作用
LibreOffice 开源办公套件,提供文档渲染与转换能力(需后台运行)
JODConverter Java 封装库,通过 UNO API 调用 LibreOffice 进行格式转换
Spring Boot 快速构建 Web 服务
Hutool 简化文件操作、路径处理、工具类封装
OSS Service 可选,用于临时文件存储或结果上传(如 MinIO、阿里云 OSS)

⚠️ 注意:JODConverter 依赖 LibreOffice 进程,必须确保服务器已安装并启动 LibreOffice 服务 (通常以 --headless --accept="socket..." 模式运行)。


三、核心实现步骤

1. 添加依赖(Maven)

复制代码
<!-- JODConverter 核心 -->
<dependency>
    <groupId>org.jodconverter</groupId>
    <artifactId>jodconverter-spring-boot-starter</artifactId>
    <version>4.4.6</version>
</dependency>

<!-- Hutool 工具包 -->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.22</version>
</dependency>

如果你使用的是 JODConverter Local (本地调用 LibreOffice),还需确保 libreoffice 已安装:

复制代码
# Ubuntu
sudo apt install libreoffice

# 后台启动 LibreOffice(无界面模式)
soffice --headless --accept="socket,host=127.0.0.1,port=2002;urp;" --nofirststartwizard &

2. 编写 Controller:接收文件并返回 PDF

复制代码
@RestController
@RequestMapping("${spring.application.name}/document/converter")
@AllArgsConstructor
@Tag(name = "DocumentConverterController", description = "文档转换API")
public class DocumentConverterController {

    private final DocumentConverterService documentConverterService;

    @PostMapping(value = "/toPdf", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    @Operation(summary = "上传任意文档,转换为PDF并下载")
    public ResponseEntity<byte[]> toPdf(@RequestPart("file") MultipartFile file) {
        byte[] pdfBytes = documentConverterService.toPdfByte(file);
        String filename = FileNameUtil.getPrefix(file.getOriginalFilename()) + "." + "pdf";
        return HttpFileUtil.downloadByte(pdfBytes, filename);
    }
}

✅ 支持 .doc, .docx, .xls, .xlsx, .ppt, .pptx 等常见格式。


3. 实现 Service:核心转换逻辑

关键方法 toPdf(MultipartFile) 流程如下:

  1. 校验文件有效性;

  2. 保存为临时文件 (因 JODConverter 只接受 File 对象);

  3. 调用 LibreOffice 转换;

  4. 读取 PDF 字节数组;

  5. 清理临时文件(防止磁盘爆满)。

    @Slf4j
    @Service
    @AllArgsConstructor
    public class DocumentConverterServiceImpl implements DocumentConverterService {

    复制代码
     private final OssProperties ossProperties;
     private final OssService ossService;
    
     private DocumentConverter getConverter() {
         try {
             DocumentConverter converter = SpringUtil.getBean(DocumentConverter.class);
             AssertUtil.notNull(converter, "请先配置LibreOffice!");
             return converter;
         } catch (NoSuchBeanDefinitionException ex) {
             throw new ServiceException("请先配置LibreOffice!");
         }
     }
    
     @Override
     public File toPdf(MultipartFile file) {
         if (file == null || file.isEmpty()) {
             throw new ServiceException("文件不能为空");
         }
         // 构建临时目录
         String tempDir = ossProperties.getLocal().getPath() + "/temp/" + IdGeneratorUtil.getSnowflakeNextIdStr();
         File inputFile = FileUtil.writeFromStream(file.getInputStream(), tempDir + "/" + file.getOriginalFilename());
         try {
             return this.toPdf(inputFile); // 重载方法
         } finally {
             FileUtil.del(tempDir); // 清理整个临时目录
         }
     }
    
     @Override
     public File toPdf(File inputFile) {
         String ext = FileNameUtil.getSuffix(inputFile.getName()).toLowerCase();
         if ("pdf".equals(ext)) {
             return inputFile; // 已是 PDF,无需转换
         }
    
         String outputPath = inputFile.getParent() + "/" + FileUtil.getPrefix(inputFile.getName()) + ".pdf";
         File outputFile = FileUtil.touch(outputPath);
    
         long start = System.currentTimeMillis();
         try {
             getConverter().convert(inputFile).to(outputFile).execute();
             log.info("转换成功,耗时 {} ms", System.currentTimeMillis() - start);
             return outputFile;
         } catch (Exception e) {
             log.error("文档转换失败", e);
             throw new ServiceException("不支持的文档格式或转换异常");
         }
     }
    
     @Override
     public byte[] toPdfByte(MultipartFile file) {
         File pdfFile = null;
         try {
             pdfFile = toPdf(file);
             return FileUtil.readBytes(pdfFile);
         } finally {
             if (pdfFile != null) {
                 FileUtil.del(pdfFile.getParentFile()); // 安全删除父目录
             }
         }
     }

    }

🔒 安全提示 :务必在 finally 块中删除临时文件,避免攻击者通过大量上传占满磁盘。


4. 工具类补充:HttpFileUtil

用于生成可下载的 PDF 响应:

复制代码
public class HttpFileUtil {
    public static ResponseEntity<byte[]> downloadByte(byte[] content, String filename) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentDispositionFormData("attachment", URLEncoder.encode(filename, StandardCharsets.UTF_8));
        headers.setContentType(MediaType.APPLICATION_PDF);
        headers.setContentLength(content.length);
        return new ResponseEntity<>(content, headers, HttpStatus.OK);
    }
}

四、部署与配置建议

1. LibreOffice 启动脚本(推荐 systemd)

创建 /etc/systemd/system/libreoffice.service:

复制代码
[Unit]
Description=LibreOffice Headless Service
After=network.target

[Service]
Type=simple
User=your-app-user
ExecStart=/usr/bin/soffice --headless --accept="socket,host=127.0.0.1,port=2002;urp;" --nofirststartwizard
Restart=always

[Install]
WantedBy=multi-user.target

然后启用:

复制代码
sudo systemctl daemon-reload
sudo systemctl enable libreoffice
sudo systemctl start libreoffice

2. JODConverter 配置(application.yml)

复制代码
jodconverter:
  local:
    office-home: /usr/lib/libreoffice  # 可选,自动检测通常足够
    port: 2002
    task-execution-timeout: 120000     # 超时2分钟

五、扩展功能(可选)

  • 异步转换 :使用 @Async 避免大文件阻塞 HTTP 请求;
  • 结果上传 OSS :如代码中的 toPdfAndUpload 方法;
  • 格式白名单校验 :防止恶意文件(如 .exe)上传;
  • 转换队列限流:避免 LibreOffice 进程过载。

六、总结

通过 Spring Boot + JODConverter + LibreOffice,我们实现了高效、稳定的文档转 PDF 能力。该方案已在多个生产系统中验证,支持高并发(配合连接池)、格式丰富、易于维护。

💡 最后提醒 :LibreOffice 是重量级进程,不要在容器中频繁启停 ,建议常驻运行;若追求轻量,可考虑 OnlyOffice 或 Pandoc 等替代方案。


GitHub 示例项目 (可自行搭建):欢迎 Star ⭐
关键词:#SpringBoot #PDF #文档转换 #LibreOffice #JODConverter #Java


如有疑问,欢迎评论区交流!如果你觉得这篇文章对你有帮助,别忘了点赞、收藏、转发~

相关推荐
daidaidaiyu8 小时前
ThingsBoard 集群的核心逻辑源码分析
java
我可能是个假开发9 小时前
FTP Unicode 文件名导致 `550` 的问题分析与解决方案
java·开发语言·spring
caoerzhong9 小时前
中小企业上 WMS 该先上哪几块:JeeWMS 开源 Java 仓库管理系统的分批上线清单
java·python·开源
【JAVA】玩家10 小时前
Spring核心原理全解析:从零到生产实战
java·后端·spring
Wang's Blog11 小时前
Java框架 SpringCloud 快速入门: 实现 Feign 最佳实践(抽取方式)
java·spring cloud
MandalaO_O12 小时前
IDEA 开发(快捷键 + 调试 + 序列化)
java·ide·intellij-idea
xiaoqiMikko12 小时前
JVM 线上排查实战(七):jps 看不见它、jstack 连不上它,可它明明活得好好的
java·jvm
狼爷13 小时前
Rust/Go/Java/Python/PHP 大比拼:负载下后端框架到底差多少?
java·后端·编程语言
念何架构之路13 小时前
zap扩展生态与总结
java·前端·数据库
第七页独白14 小时前
汽车零件厂如何通过 QMS 真正落地 IATF 16949——QMS软件系统:品质检验-内审稽核-8d客诉管理:全星质量管理软件系统
java·前端·数据库