Spring Boot 整合 LibreOffice:本地调用、远程服务与 Docker 一体化部署实战

Spring Boot 整合 LibreOffice:本地调用、远程服务与 Docker 一体化部署实战

在企业级应用中,文档格式转换(如 Word 转 PDF、Excel 转 HTML)是常见需求。LibreOffice 作为开源、免费且功能强大的办公套件,提供了命令行接口,非常适合集成到 Java 应用中实现自动化文档处理。

本文将详细介绍 Spring Boot 项目整合 LibreOffice 的两种主流方式

  1. 调用本地安装的 LibreOffice(适用于单机或轻量场景)
  2. 调用远程 LibreOffice 服务(适用于微服务、高并发或容器化架构)

并进一步演示如何通过 Docker Compose 将 Spring Boot 应用与 LibreOffice 容器 一体化部署,实现开箱即用的文档转换服务。


一、准备工作

技术栈

  • Spring Boot 3.x(Java 17+)
  • LibreOffice 7.0+
  • Docker & Docker Compose
  • Apache Commons IO(用于文件操作)

Maven 依赖(pom.xml

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.15.1</version>
</dependency>

二、方式一:调用本地 LibreOffice(进程调用)

适用于开发环境或单机部署,直接通过 ProcessBuilder 调用系统中已安装的 LibreOffice。

1. 安装 LibreOffice(Linux 示例)

csharp 复制代码
# Ubuntu/Debian
sudo apt-get install libreoffice

# CentOS/RHEL
sudo yum install libreoffice-headless

⚠️ 生产环境建议安装 libreoffice-headless(无图形界面版本),节省资源。

2. 编写转换工具类

arduino 复制代码
@Service
public class LocalLibreOfficeService {

    private static final String LIBREOFFICE_PATH = "/usr/bin/libreoffice";

    public void convertToPdf(File inputFile, File outputFile) throws IOException, InterruptedException {
        String outputDir = outputFile.getParent();

        ProcessBuilder builder = new ProcessBuilder(
            LIBREOFFICE_PATH,
            "--headless",
            "--convert-to", "pdf",
            "--outdir", outputDir,
            inputFile.getAbsolutePath()
        );

        Process process = builder.start();
        int exitCode = process.waitFor();

        if (exitCode != 0) {
            throw new RuntimeException("LibreOffice conversion failed with exit code: " + exitCode);
        }

        // 重命名生成的文件(LibreOffice 默认保留原文件名)
        File generated = new File(outputDir, 
            FilenameUtils.getBaseName(inputFile.getName()) + ".pdf");
        if (!generated.renameTo(outputFile)) {
            throw new RuntimeException("Failed to rename output file");
        }
    }
}

3. 控制器示例

less 复制代码
@PostMapping("/convert/local")
public ResponseEntity<Resource> convertLocal(@RequestParam("file") MultipartFile file) 
    throws IOException, InterruptedException {
    
    File input = File.createTempFile("input_", "." + getExtension(file.getOriginalFilename()));
    File output = File.createTempFile("output_", ".pdf");

    file.transferTo(input);
    localLibreOfficeService.convertToPdf(input, output);

    return ResponseEntity.ok()
        .contentType(MediaType.APPLICATION_PDF)
        .body(new ByteArrayResource(Files.readAllBytes(output.toPath())));
}

优点 :简单直接,无需额外服务

缺点:耦合本地环境,难以横向扩展,多进程竞争资源


三、方式二:调用远程 LibreOffice 服务(推荐生产使用)

将 LibreOffice 封装为独立的 REST 服务(或使用现成方案如 unoconv + FlaskGotenberg),Spring Boot 通过 HTTP 调用。

本文采用 Gotenberg ------ 一个基于 Docker 的现代化文档转换微服务,支持 LibreOffice、Chrome 等引擎。

1. 启动 Gotenberg 服务(Docker)

bash 复制代码
docker run --rm -p 3000:3000 thecodingmachine/gotenberg:8

Gotenberg 提供 /forms/libreoffice/convert 接口用于文档转换。

2. Spring Boot 调用远程服务

arduino 复制代码
@Service
public class RemoteLibreOfficeService {

    private final RestTemplate restTemplate;
    private static final String GOTENBERG_URL = "http://localhost:3000";

    public RemoteLibreOfficeService(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    public byte[] convertToPdf(MultipartFile file) {
        LinkedMultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
        map.add("files", new ByteArrayResource(file.getBytes()) {
            @Override
            public String getFilename() {
                return file.getOriginalFilename();
            }
        });

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);

        HttpEntity<LinkedMultiValueMap<String, Object>> request = 
            new HttpEntity<>(map, headers);

        return restTemplate.postForObject(
            GOTENBERG_URL + "/forms/libreoffice/convert",
            request,
            byte[].class
        );
    }
}

3. 控制器调用

less 复制代码
@PostMapping("/convert/remote")
public ResponseEntity<Resource> convertRemote(@RequestParam("file") MultipartFile file) {
    byte[] pdfBytes = remoteLibreOfficeService.convertToPdf(file);
    return ResponseEntity.ok()
        .contentType(MediaType.APPLICATION_PDF)
        .body(new ByteArrayResource(pdfBytes));
}

优点

  • 解耦应用与 LibreOffice
  • 可独立扩缩容
  • 支持多种格式(PDF、PNG、TXT 等)
  • 内置重试、超时、限流机制(可通过 Nginx 或服务网格增强)

四、Docker 一体化部署:Spring Boot + LibreOffice

为了实现"一键部署",我们使用 Docker Compose 同时启动应用和 LibreOffice 服务。

方案选择:Gotenberg 容器 + 自定义 Spring Boot 容器

1. Dockerfile(Spring Boot 应用)

bash 复制代码
FROM eclipse-temurin:17-jre-alpine
COPY target/app.jar /app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app.jar"]

2. docker-compose.yml

yaml 复制代码
version: '3.8'

services:
  app:
    build: .
    ports:
      - "8080:8080"
    depends_on:
      - gotenberg
    environment:
      - GOTENBERG_URL=http://gotenberg:3000

  gotenberg:
    image: thecodingmachine/gotenberg:8
    ports:
      - "3000:3000"

3. 修改 Spring Boot 配置(支持动态 URL)

ruby 复制代码
# application.yml
gotenberg:
  url: ${GOTENBERG_URL:http://localhost:3000}

并在 RemoteLibreOfficeService 中注入配置:

kotlin 复制代码
@Value("${gotenberg.url}")
private String gotenbergUrl;

4. 构建并启动

lua 复制代码
./mvnw clean package -DskipTests
docker-compose up --build

访问:

  • 应用:http://localhost:8080/convert/remote
  • Gotenberg UI:http://localhost:3000

五、对比总结

方式 适用场景 扩展性 维护成本 推荐度
本地调用 开发、测试、小型单机应用 ⭐⭐
远程服务(Gotenberg) 微服务、生产环境、高并发 ⭐⭐⭐⭐⭐
Docker 一体化 快速交付、CI/CD、演示环境 低(容器化后) ⭐⭐⭐⭐

六、注意事项

  1. 文件安全:上传文件需校验类型、大小,防止恶意文档攻击。
  2. 临时文件清理 :使用 @Scheduled 定期清理 /tmp 中的临时文件。
  3. 超时控制:LibreOffice 转换大文件可能耗时较长,需设置合理超时(如 60s)。
  4. 并发限制:LibreOffice 单实例并发能力有限,高并发场景建议部署多个 Gotenberg 实例 + 负载均衡。

结语

通过本文,你已掌握 Spring Boot 与 LibreOffice 集成的 三种部署形态:从简单的本地调用,到解耦的远程服务,再到容器化的一体化部署。无论你是构建内部工具还是对外 SaaS 产品,都能找到合适的方案。

相关推荐
oak隔壁找我1 天前
MySQL中 SHOW FULL PROCESSLIST` 输出中 `State` 列的所有可能值
后端
上进小菜猪1 天前
基于 YOLOv8 的面向文档智能处理的表格区域检测系统 [目标检测完整源码]
后端
oak隔壁找我1 天前
JVM常用调优参数
java·后端
IT_陈寒1 天前
React状态管理终极对决:Redux vs Context API谁更胜一筹?
前端·人工智能·后端
晨星shine1 天前
GC、Dispose、Unmanaged Resource 和 Managed Resource
后端·c#
蝎子莱莱爱打怪1 天前
OpenClaw 从零配置指南:接入飞书 + 常用命令 + 原理图解
java·后端·ai编程
倚栏听风雨1 天前
【ES避坑指南】明明存的是 "CodingAddress",为什么 term 查询死活查不到?彻底搞懂 text 和 keyword
后端
程序员爱钓鱼1 天前
Go 操作 Windows COM 自动化实战:深入解析 go-ole
后端·go·排序算法
回家路上绕了弯1 天前
深入解析Agent Subagent架构:原理、协同逻辑与实战落地指南
分布式·后端
子玖1 天前
实现微信扫码注册登录-基于参数二维码
后端·微信·go