当前位置: 首页 > news >正文

使用LibreOffice将office相关文件(.xls/.xlsx/.doc/.docx)转为pdf

LibreOffice

下载地址https://www.libreoffice.org/download/

Java代码示例

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;/*** 使用本机 LibreOffice headless 将 Office 文件转换为 PDF。*/
@Slf4j
@Component
public class LibreOfficePdfConverter {@Value("${file.preview.libre-office.enabled:true}")private boolean enabled;@Value("${file.preview.libre-office.soffice-path:E:\\LibreOffice\\program\\soffice.exe}")private String sofficePath;@Value("${file.preview.libre-office.temp-dir:E:\\libreOfficeTemp}")private String tempDir;@Value("${file.preview.libre-office.timeout-seconds:60}")private long timeoutSeconds;public Optional<byte[]> tryConvertToPdf(byte[] fileBytes, String suffix) {if (!enabled) {return Optional.empty();}if (fileBytes == null || fileBytes.length == 0 || StringUtils.isBlank(suffix)) {return Optional.empty();}Path soffice = Paths.get(sofficePath);if (!Files.isRegularFile(soffice)) {log.warn("LibreOffice soffice不存在,跳过本地转换: {}", sofficePath);return Optional.empty();}Path workDir = null;try {workDir = createWorkDir();String normalizedSuffix = suffix.toLowerCase(Locale.ROOT);Path inputFile = workDir.resolve("source." + normalizedSuffix);Path profileDir = workDir.resolve("profile");Files.createDirectories(profileDir);Files.write(inputFile, fileBytes);Path outputLog = workDir.resolve("soffice.log");List<String> command = Arrays.asList(sofficePath,"--headless","--nologo","--nofirststartwizard","--norestore","-env:UserInstallation=" + profileDir.toUri(),"--convert-to","pdf","--outdir",workDir.toString(),inputFile.toString());Process process = new ProcessBuilder(command).redirectErrorStream(true).redirectOutput(outputLog.toFile()).start();boolean finished = process.waitFor(timeoutSeconds, TimeUnit.SECONDS);if (!finished) {process.destroyForcibly();log.warn("LibreOffice转PDF超时,suffix={}, timeoutSeconds={}", suffix, timeoutSeconds);return Optional.empty();}if (process.exitValue() != 0) {log.warn("LibreOffice转PDF失败,exitCode={}, output={}", process.exitValue(), readLog(outputLog));return Optional.empty();}Path pdfFile = resolvePdfFile(workDir);if (pdfFile == null || !Files.isRegularFile(pdfFile)) {log.warn("LibreOffice转PDF未生成PDF,output={}", readLog(outputLog));return Optional.empty();}return Optional.of(Files.readAllBytes(pdfFile));} catch (Exception ex) {log.warn("LibreOffice转PDF异常,suffix={}", suffix, ex);return Optional.empty();} finally {deleteQuietly(workDir);}}private Path createWorkDir() throws IOException {if (StringUtils.isBlank(tempDir)) {return Files.createTempDirectory("private-product-preview-");}Path baseDir = Paths.get(tempDir);Files.createDirectories(baseDir);return Files.createTempDirectory(baseDir, "private-product-preview-");}private Path resolvePdfFile(Path workDir) throws IOException {Path expectedFile = workDir.resolve("source.pdf");if (Files.isRegularFile(expectedFile)) {return expectedFile;}try (Stream<Path> stream = Files.list(workDir)) {return stream.filter(path -> StringUtils.endsWithIgnoreCase(path.getFileName().toString(), ".pdf")).findFirst().orElse(null);}}private String readLog(Path outputLog) {try {if (!Files.isRegularFile(outputLog)) {return "";}String text = new String(Files.readAllBytes(outputLog), StandardCharsets.UTF_8);return StringUtils.left(text, 2000);} catch (Exception ignored) {return "";}}private void deleteQuietly(Path workDir) {if (workDir == null || !Files.exists(workDir)) {return;}try (Stream<Path> stream = Files.walk(workDir)) {stream.sorted(Comparator.reverseOrder()).forEach(path -> {try {Files.deleteIfExists(path);} catch (IOException ignored) {// 临时文件清理失败不影响预览结果。
                        }});} catch (IOException ignored) {// 临时目录清理失败不影响预览结果。
        }}
}

注意

依赖软件:
1. LibreOffice
- 需要包含 soffice 命令
- Linux 推荐安装 libreoffice、libreoffice-writer、libreoffice-calc
- 应用配置中的 soffice-path 需要指向 soffice 可执行文件
 
2. 中文字体
- 至少安装 Noto CJK 字体,例如 fonts-noto-cjk
- 如需最大程度还原 Windows/WPS 文档效果,需要补充业务文档中使用的字体,例如宋体、黑体、仿宋、楷体、微软雅黑等
- 字体安装后需要刷新 fontconfig 缓存
 
3. 临时目录
- 应用需要一个可读写的临时目录
- 推荐:/tmp/private-product-preview
 
如果是 K8s 容器部署,建议打进后端镜像里,不要运行时手工装:
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
libreoffice \
libreoffice-writer \
libreoffice-calc \
fonts-noto-cjk \
fontconfig \
&& fc-cache -fv \
&& rm -rf /var/lib/apt/lists/*
yaml配置:
file:preview:libre-office:enabled: truesoffice-path: /usr/bin/sofficetemp-dir: /tmp/private-product-previewtimeout-seconds: 60                        

 

http://www.jsqmd.com/news/764373/

相关文章:

  • 无需安装!用快马平台5分钟快速原型一个Flask待办事项应用
  • 新手福音:用快马AI生成你的第一份pytest测试代码
  • 如何永久备份微信聊天记录:WeChatMsg免费完整解决方案终极指南
  • 企业内如何通过Taotoken实现API Key的集中管理与访问审计
  • 从一次内存泄漏排查说起:深入理解UE5中FName的全局表与FString的陷阱
  • BilibiliDown终极指南:告别在线限制,4种实用方法轻松收藏B站精彩内容
  • 单片机开发者如何通过curl快速测试Taotoken大模型API的稳定性与延迟
  • 20个Illustrator脚本终极指南:从设计新手到效率大师的完整教程
  • H5GG iOS模组引擎完整指南:用JavaScript轻松修改iOS游戏
  • ACM训练问题实际代码操作
  • MCP 2026容器化国产部署失效真相(OpenEuler 22.03 LTS + iSulad + 国产K8s发行版适配断点图谱)
  • 2026年200G光模块品牌推荐:主流厂商测评与高性价比选型指南 - 博客湾
  • SCMP证书多久拿到手? - 众智商学院官方
  • 音乐格式壁垒终结者:Unlock-Music让你的数字音乐真正属于你
  • 推来客网络:扎根成都,打造小程序开发 + 软件定制开发标杆服务商 - 资讯焦点
  • Silk v3音频解码器:轻松解决微信QQ语音格式不兼容问题
  • 首驱S300还值得买吗?适合谁、该不该等、哪些参数需要确认 - 博客万
  • 使用 TaoToken CLI 工具一键为团队统一开发环境配置模型密钥
  • LeagueAkari:如何用本地化智能工具提升你的英雄联盟游戏体验?
  • 现代全栈开发环境搭建:Next.js + Supabase + Resend + Stripe 实战指南
  • 动态上下文记忆管理:突破LLM对话限制的工程实践
  • Unity Prefab进阶玩法:用Prefab Variant和Nested Prefab管理你的复杂游戏场景
  • 2026年4月国内靠谱的梯控系统源头厂家口碑推荐,温感探测器/4G烟雾报警器/智慧楼宇梯控系统,梯控系统供应厂家哪家靠谱 - 品牌推荐师
  • 回森客服人工咨询AI流量赋能,重塑智能科技高效与便捷体验新标杆 - 资讯焦点
  • 上海泽固新型建材:静安抢修料批发选哪家 - LYL仔仔
  • Python子进程管理避坑指南:wait()会卡死?terminate()不灵?一次讲清Popen的正确关闭姿势
  • JenkinsExploit-GUI从下载到打包:避坑指南与自定义Payload集成教程
  • 五一随感
  • 2026年AI模型API中转系统年度测评:五大平台硬核数据对比,为开发者提供权威选型指南
  • 换新手机前必看:保姆级微信数据迁移避坑指南(防中断、防失败、防丢失)