基于 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 是重量级进程,不要在容器中频繁启停 ,建议常驻运行;若追求轻量,可考虑 OnlyOfficePandoc 等替代方案。


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


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

相关推荐
寻星探路5 小时前
【算法专题】哈希表:从“两数之和”到“最长连续序列”的深度解析
java·数据结构·人工智能·python·算法·ai·散列表
q***44155 小时前
SpringSecurity踢出指定用户
java
SHolmes18545 小时前
Python all函数 判断是否同时满足多个条件
java·服务器·python
shejizuopin5 小时前
基于JavaSSM+MySQL的实验室考勤管理系统设计与实现
java·mysql·vue·毕业设计·论文·springboot·实验室考勤管理系统设计与实现
J***51685 小时前
SpringSecurity的配置
java
面汤放盐6 小时前
软件架构指南 Software Architecture Guide
java·微服务·devops
tkevinjd6 小时前
JUC5(线程池)
java·线程池·多线程·juc
Tao____6 小时前
如何对接Modbus-tcp协议(使用Thinlinks物联网平台)
java·物联网·网络协议·tcp/ip·modbus
鱼跃鹰飞6 小时前
经典面试题:K8S的自动缩扩容和崩溃恢复
java·容器·kubernetes