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


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

相关推荐
aithinker1 分钟前
使用QQ邮箱收发邮件遇到的坑 有些WIFI不支持ipv6
java
星火开发设计18 分钟前
C++ 预处理指令:#include、#define 与条件编译
java·开发语言·c++·学习·算法·知识
Hx_Ma1636 分钟前
SpringMVC返回值
java·开发语言·servlet
Yana.nice39 分钟前
openssl将证书从p7b转换为crt格式
java·linux
独自破碎E42 分钟前
【滑动窗口+字符计数数组】LCR_014_字符串的排列
android·java·开发语言
想逃离铁厂的老铁1 小时前
Day55 >> 并查集理论基础 + 107、寻找存在的路线
java·服务器
Jack_David1 小时前
Java如何生成Jwt之使用Hutool实现Jwt
java·开发语言·jwt
瑞雪兆丰年兮1 小时前
[从0开始学Java|第六天]Java方法
java·开发语言
一点技术1 小时前
基于SpringBoot的选课调查系统
java·spring boot·后端·选课调查系统
datalover1 小时前
CompletableFuture 使用示例
java·开发语言