基于 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


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

相关推荐
一定要AK7 小时前
Spring 入门核心笔记
java·笔记·spring
A__tao7 小时前
Elasticsearch Mapping 一键生成 Java 实体类(支持嵌套 + 自动过滤注释)
java·python·elasticsearch
KevinCyao8 小时前
java视频短信接口怎么调用?SpringBoot集成视频短信及回调处理Demo
java·spring boot·音视频
迷藏4948 小时前
**发散创新:基于Rust实现的开源合规权限管理框架设计与实践**在现代软件架构中,**权限控制(RBAC)** 已成为保障
java·开发语言·python·rust·开源
wuxinyan1239 小时前
Java面试题47:一文深入了解Nginx
java·nginx·面试题
新知图书9 小时前
搭建Spring Boot开发环境
java·spring boot·后端
冰河团队9 小时前
一个拉胯的分库分表方案有多绝望?整个部门都在救火!
java·高并发·分布式数据库·分库分表·高性能
洛_尘9 小时前
Java EE进阶:Linux的基本使用
java·java-ee
宸津-代码粉碎机9 小时前
Spring Boot 4.0虚拟线程实战调优技巧,最大化发挥并发优势
java·人工智能·spring boot·后端·python
MaCa .BaKa9 小时前
47-心里健康咨询平台/心理咨询系统
java·spring boot·mysql·tomcat·maven·intellij-idea·个人开发