libreoffice容器word转pdf

[复制链接]
发表于 2025-7-31 14:43:23 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
先说结论,市面上不费钱的,简单的结果好的就是这个种方式,在线测试下来不如命令转的结果好。AsposeWords和SpireDoc结果都不错,但是只有这个word转pdf感觉花3-5w不划算。
下载容器路径 https://docker.aityp.com/i/search?search=libreoffice
摆设LibreOffice容器
利用Docker运行LibreOffice的无头模式(headless),提供文档转换服务:
  1. #需要挂载输入输出路径和安装字体路径
  2. docker run -d \
  3. --name libreoffice1 \
  4. -v /opt/libreoffice1/input:/app/input \
  5. -v /opt/libreoffice1/output:/app/output \
  6. -v /usr/share/fonts/:/usr/share/fonts/
  7. -p 3000:3000 \
  8. linuxserver/libreoffice:latest
  9. #online用的是 需要注意容器配置文件有个位置需要改成一下 要不然http访问不通
  10. docker run -t -d -p 9980:9980 -e "username=admin" -e "password=123456" --restart always --cap-add SYS_ADMIN libreofficeonline:telecom
复制代码
此命令启动一个LibreOffice容器,监听8100端口,并将宿主机目录挂载到容器内以便文件互换。
Java调用REST API转换文档
若容器提供REST API(如libreserver/office-api),可通过Java的HTTP客户端发送请求:
  1. package cn.zjtele.pubinfo.demo.api.controller;
  2. import org.apache.http.HttpEntity;
  3. import org.apache.http.client.config.RequestConfig;
  4. import org.apache.http.client.methods.CloseableHttpResponse;
  5. import org.apache.http.client.methods.HttpPost;
  6. import org.apache.http.conn.ssl.NoopHostnameVerifier;
  7. import org.apache.http.entity.ContentType;
  8. import org.apache.http.entity.mime.MultipartEntityBuilder;
  9. import org.apache.http.impl.client.CloseableHttpClient;
  10. import org.apache.http.impl.client.HttpClients;
  11. import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
  12. import org.apache.http.ssl.SSLContexts;
  13. import org.apache.http.util.EntityUtils;
  14. import org.slf4j.MDC;
  15. import javax.net.ssl.SSLContext;
  16. import java.io.File;
  17. import java.io.FileOutputStream;
  18. import java.nio.charset.StandardCharsets;
  19. import java.security.KeyManagementException;
  20. import java.security.KeyStoreException;
  21. import java.security.NoSuchAlgorithmException;
  22. import java.util.HashMap;
  23. import java.util.Map;
  24. import java.util.UUID;
  25. import java.util.concurrent.ConcurrentHashMap;
  26. import java.util.concurrent.Future;
  27. import java.util.concurrent.ThreadPoolExecutor;
  28. import java.util.concurrent.TimeUnit;
  29. import java.util.concurrent.TimeoutException;
  30. import static com.sun.javafx.runtime.async.BackgroundExecutor.getExecutor;
  31. public class LibreOfficeOnlineMasterConverter {
  32.     // 正确的API端点路径(根据您的服务器配置可能需要调整)
  33.     private static final String LOOL_CONVERT_URL = "http://localhost:9980/lool/convert-to/pdf";
  34.     // 如果需要忽略SSL证书验证
  35.     static SSLContext sslContext;
  36.     static {
  37.         try {
  38.             sslContext = SSLContexts.custom()
  39.                     .loadTrustMaterial((chain, authType) -> true)
  40.                     .build();
  41.         } catch (NoSuchAlgorithmException e) {
  42.             throw new RuntimeException(e);
  43.         } catch (KeyManagementException e) {
  44.             throw new RuntimeException(e);
  45.         } catch (KeyStoreException e) {
  46.             throw new RuntimeException(e);
  47.         }
  48.     }
  49.     // 在类初始化时创建共享的HttpClient
  50.     private static final CloseableHttpClient sharedHttpClient = HttpClients.custom()
  51.             .setSSLContext(sslContext)
  52.             .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
  53.             .setMaxConnTotal(100)  // 最大连接数
  54.             .setMaxConnPerRoute(20) // 每个路由最大连接数
  55.             .build();
  56.     public static void printPoolStatus() {
  57.         ThreadPoolExecutor executor = (ThreadPoolExecutor) getExecutor();
  58.         System.out.println("活跃线程: " + executor.getActiveCount() +
  59.                 " / 队列任务: " + executor.getQueue().size());
  60.     }
  61.     public static boolean convertToPdf(String inputFile, String outputFile) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
  62.         MDC.put("traceId", UUID.randomUUID().toString().substring(0,8));
  63.         System.out.println("开始处理文件: " + inputFile);
  64.         // 如果需要忽略SSL证书验证
  65. //        SSLContext sslContext = SSLContexts.custom()
  66. //                .loadTrustMaterial((chain, authType) -> true)
  67. //                .build();
  68.         // 修改convertToPdf方法中的httpClient获取方式
  69. //        CloseableHttpClient httpClient = sharedHttpClient;
  70.         // 调整HttpClient配置,增加超时控制
  71.         RequestConfig config = RequestConfig.custom()
  72.                 .setConnectTimeout(5000)       // 连接超时5秒
  73.                 .setSocketTimeout(30000)       // 数据传输超时30秒
  74.                 .build();
  75.         CloseableHttpClient httpClient = HttpClients.custom()
  76.                 .setDefaultRequestConfig(config)
  77.                 .setConnectionManager(new PoolingHttpClientConnectionManager()) // 使用连接池
  78.                 .build();
  79.         try {
  80.             // 1. 创建POST请求
  81.             HttpPost httpPost = new HttpPost(LOOL_CONVERT_URL);
  82.             // 2. 构建Multipart请求体(尝试不同字段名)
  83.             MultipartEntityBuilder builder = MultipartEntityBuilder.create();
  84.             builder.addBinaryBody(
  85.                     "file",  // 先尝试"file",如果失败再尝试"data"
  86.                     new File(inputFile),
  87.                     getContentType(inputFile),
  88.                     new File(inputFile).getName()
  89.             );
  90.             // 3. 设置必要的头信息(master分支特定头)
  91.             httpPost.setHeader("X-WOPI-Override", "CONVERT_TO");
  92.             httpPost.setHeader("X-WOPI-FileExtension", getFileExtension(inputFile));
  93.             httpPost.setHeader("X-WOPI-SuggestedTarget", getOutputFilename(outputFile));
  94.             httpPost.setHeader("X-LOOL-WOPI-ConvertTo", "pdf");  // master分支特有
  95.             httpPost.setHeader("Accept", "application/pdf");
  96.             // 4. 添加其他可能的必要头
  97.             httpPost.setHeader("User-Agent", "Java LibreOffice Converter");
  98.             httpPost.setHeader("Cache-Control", "no-cache");
  99.             httpPost.setEntity(builder.build());
  100.             System.out.println("发送请求到: " + LOOL_CONVERT_URL);
  101.             System.out.println("使用头信息: " + httpPost.getAllHeaders());
  102.             // 5. 执行请求
  103.             try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
  104.                 int statusCode = response.getStatusLine().getStatusCode();
  105.                 HttpEntity entity = response.getEntity();
  106.                 System.out.println("响应状态: " + response.getStatusLine());
  107.                 System.out.println("响应头: " + response.getAllHeaders());
  108.                 if (statusCode == 200 && entity != null) {
  109.                     try (FileOutputStream fos = new FileOutputStream(outputFile)) {
  110.                         entity.writeTo(fos);
  111.                     }
  112.                     return true;
  113.                 } else {
  114.                     String responseBody = entity != null ?
  115.                             EntityUtils.toString(entity, StandardCharsets.UTF_8) : "无响应体";
  116.                     System.err.println("转换失败. 状态码: " + statusCode);
  117.                     System.err.println("响应体: " + responseBody);
  118.                     // 如果400错误,尝试使用"data"作为字段名
  119.                     if (statusCode == 400) {
  120.                         System.out.println("尝试使用'data'作为字段名重试...");
  121.                         return retryWithDataField(inputFile, outputFile);
  122.                     }
  123.                 }
  124.             }
  125.         } catch (Exception e) {
  126.             System.err.println("转换过程中发生错误: " + e.getMessage());
  127.             e.printStackTrace();
  128.         } finally {
  129.             try {
  130.                 httpClient.close();
  131.             } catch (Exception e) {
  132.                 System.err.println("关闭HTTP客户端时出错: " + e.getMessage());
  133.             }
  134.         }
  135.         return false;
  136.     }
  137.     /**
  138.      * 使用"data"作为字段名重试
  139.      */
  140.     private static boolean retryWithDataField(String inputFile, String outputFile) {
  141.         CloseableHttpClient httpClient = HttpClients.createDefault();
  142.         try {
  143.             HttpPost httpPost = new HttpPost(LOOL_CONVERT_URL);
  144.             MultipartEntityBuilder builder = MultipartEntityBuilder.create();
  145.             builder.addBinaryBody(
  146.                     "data",  // 使用"data"作为字段名
  147.                     new File(inputFile),
  148.                     getContentType(inputFile),
  149.                     new File(inputFile).getName()
  150.             );
  151.             // 设置相同的头信息
  152.             httpPost.setHeader("X-WOPI-Override", "CONVERT_TO");
  153.             httpPost.setHeader("X-WOPI-FileExtension", getFileExtension(inputFile));
  154.             httpPost.setHeader("X-WOPI-SuggestedTarget", getOutputFilename(outputFile));
  155.             httpPost.setHeader("X-LOOL-WOPI-ConvertTo", "pdf");
  156.             httpPost.setHeader("Accept", "application/pdf");
  157.             httpPost.setEntity(builder.build());
  158.             try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
  159.                 if (response.getStatusLine().getStatusCode() == 200) {
  160.                     try (FileOutputStream fos = new FileOutputStream(outputFile)) {
  161.                         response.getEntity().writeTo(fos);
  162.                     }
  163.                     return true;
  164.                 }
  165.             }
  166.         } catch (Exception e) {
  167.             System.err.println("重试失败: " + e.getMessage());
  168.         }
  169.         return false;
  170.     }
  171.     // 新增异步转换方法
  172.     public static Future<Boolean> convertToPdfAsync(String inputFile, String outputFile) {
  173.         return ConverterThreadPool.getExecutor().submit(() -> {
  174.             try {
  175.                 return convertToPdf(inputFile, outputFile);
  176.             } catch (Exception e) {
  177.                 System.err.println("异步任务执行异常: " + e.getMessage());
  178.                 return false;
  179.             }
  180.         });
  181.     }
  182.     // 新增批量处理方法
  183.     public static Map<String, Future<Boolean>> batchConvert(Map<String, String> filePairs) {
  184.         Map<String, Future<Boolean>> results = new ConcurrentHashMap<>();
  185.         filePairs.forEach((input, output) ->
  186.                 results.put(input, convertToPdfAsync(input, output))
  187.         );
  188.         return results;
  189.     }
  190.     /**
  191.      * 获取正确的内容类型
  192.      */
  193.     private static ContentType getContentType(String filePath) {
  194.         String ext = getFileExtension(filePath).toLowerCase();
  195.         switch (ext) {
  196.             case "docx": return ContentType.create("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
  197.             case "doc": return ContentType.create("application/msword");
  198.             case "odt": return ContentType.create("application/vnd.oasis.opendocument.text");
  199.             default: return ContentType.APPLICATION_OCTET_STREAM;
  200.         }
  201.     }
  202.     private static String getFileExtension(String filePath) {
  203.         int lastDotIndex = filePath.lastIndexOf('.');
  204.         return lastDotIndex > 0 ? filePath.substring(lastDotIndex + 1) : "";
  205.     }
  206.     private static String getOutputFilename(String filePath) {
  207.         return new File(filePath).getName();
  208.     }
  209.     public static void main(String[] args) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
  210.     String inputFile = "C:\\Users\\sheng\\Desktop\\chongqing.docx";
  211.     String outputFile = "C:\\Users\\sheng\\Desktop\\chongqing.pdf";
  212.         System.out.println("开始转换: " + inputFile + " → " + outputFile);
  213.         boolean b = convertToPdf(inputFile, outputFile);
  214.         System.out.println("转换结果: " + b);
  215.     }
  216. }
复制代码
通过命令行调用容器内工具
若容器仅包含LibreOffice命令行工具,可通过Java实行Docker命令完成转换:
  1. package cn.zjtele.pubinfo.demo.wordtopdf;
  2. import java.io.File;
  3. import java.io.IOException;
  4. import java.nio.file.Files;
  5. import java.nio.file.Path;
  6. import java.nio.file.Paths;
  7. public class LibreOfficeConverter {
  8.     private static final String INPUT_DIR = "D:/docker/input";  // 本地输入目录
  9.     private static final String OUTPUT_DIR = "D:/docker/output"; // 本地输出目录
  10.     public static void main(String[] args) {
  11. //        if (args.length == 0) {
  12. //            System.out.println("请提供要转换的Word文件名(例如:example.docx)");
  13. //            return;
  14. //        }
  15.         long l = System.currentTimeMillis();
  16.         String fileName = "11.docx";
  17.         Path inputFilePath = Paths.get(INPUT_DIR, fileName);
  18.         File inputFile = inputFilePath.toFile();
  19.         if (!inputFile.exists()) {
  20.             System.out.println("文件不存在:" + inputFilePath);
  21.             return;
  22.         }
  23.         try {
  24.             // 确保输出目录存在
  25.             Files.createDirectories(Paths.get(OUTPUT_DIR));
  26.             // 构造输出文件路径
  27.             String outputFileName = fileName.replace(".docx", ".pdf");
  28.             Path outputFilePath = Paths.get(OUTPUT_DIR, outputFileName);
  29.             // 调用 LibreOffice 容器进行转换
  30.             convertFileUsingLibreOffice(inputFile.getAbsolutePath(), outputFilePath.toString());
  31.             System.out.println("文件转换成功!PDF文件已保存到:" + outputFilePath);
  32.             System.out.println("转换耗时:" + (System.currentTimeMillis() - l) + "ms");
  33.         } catch (Exception e) {
  34.             e.printStackTrace();
  35.             System.out.println("文件转换失败!");
  36.         }
  37.     }
  38.     private static void convertFileUsingLibreOffice(String inputFilePath, String outputFilePath) throws IOException, InterruptedException {
  39.         // 使用 LibreOffice 容器命令进行转换
  40.         String command = String.format(
  41. //                "docker exec -i another_linuxserver-libreoffice libreoffice --headless --convert-to pdf --outdir /app/output /app/input/%s",
  42. //                new File(inputFilePath).getName()
  43.                 "docker exec -i libreoffice767 libreoffice --headless --convert-to pdf --outdir /app/output /app/input/%s",
  44.                 new File(inputFilePath).getName()
  45.         );
  46.         Process process = Runtime.getRuntime().exec(command);
  47.         int exitCode = process.waitFor();
  48.         if (exitCode != 0) {
  49.             throw new RuntimeException("LibreOffice 转换失败,退出码:" + exitCode);
  50.         }
  51.     }
  52. }
复制代码
文件路径处置处罚留意事项
确保Java应用有权限访问宿主机和容器的挂载目录。
输入/输出路径需利用容器内的映射路径(如/opt/documents)。
转换完成后从挂载目录提取PDF文件。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
继续阅读请点击广告
回复

使用道具 举报

×
登录参与点评抽奖,加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表