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 产品,都能找到合适的方案。

相关推荐
青云计划9 小时前
知光项目知文发布模块
java·后端·spring·mybatis
Victor3569 小时前
MongoDB(9)什么是MongoDB的副本集(Replica Set)?
后端
Victor3569 小时前
MongoDB(8)什么是聚合(Aggregation)?
后端
yeyeye11111 小时前
Spring Cloud Data Flow 简介
后端·spring·spring cloud
Tony Bai11 小时前
告别 Flaky Tests:Go 官方拟引入 testing/nettest,重塑内存网络测试标准
开发语言·网络·后端·golang·php
+VX:Fegn089512 小时前
计算机毕业设计|基于springboot + vue鲜花商城系统(源码+数据库+文档)
数据库·vue.js·spring boot·后端·课程设计
程序猿阿伟12 小时前
《GraphQL批处理与全局缓存共享的底层逻辑》
后端·缓存·graphql
小小张说故事12 小时前
SQLAlchemy 技术入门指南
后端·python
识君啊12 小时前
SpringBoot 事务管理解析 - @Transactional 的正确用法与常见坑
java·数据库·spring boot·后端
想用offer打牌13 小时前
MCP (Model Context Protocol) 技术理解 - 第五篇
人工智能·后端·mcp