Spring Boot 整合 LibreOffice:本地调用、远程服务与 Docker 一体化部署实战
在企业级应用中,文档格式转换(如 Word 转 PDF、Excel 转 HTML)是常见需求。LibreOffice 作为开源、免费且功能强大的办公套件,提供了命令行接口,非常适合集成到 Java 应用中实现自动化文档处理。
本文将详细介绍 Spring Boot 项目整合 LibreOffice 的两种主流方式:
- 调用本地安装的 LibreOffice(适用于单机或轻量场景)
- 调用远程 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 + Flask 或 Gotenberg),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、演示环境 | 高 | 低(容器化后) | ⭐⭐⭐⭐ |
六、注意事项
- 文件安全:上传文件需校验类型、大小,防止恶意文档攻击。
- 临时文件清理 :使用
@Scheduled定期清理/tmp中的临时文件。 - 超时控制:LibreOffice 转换大文件可能耗时较长,需设置合理超时(如 60s)。
- 并发限制:LibreOffice 单实例并发能力有限,高并发场景建议部署多个 Gotenberg 实例 + 负载均衡。
结语
通过本文,你已掌握 Spring Boot 与 LibreOffice 集成的 三种部署形态:从简单的本地调用,到解耦的远程服务,再到容器化的一体化部署。无论你是构建内部工具还是对外 SaaS 产品,都能找到合适的方案。