package com.estate.expert.admin.utils; import com.estate.expert.admin.utils.FileDownloader; import org.springframework.stereotype.Component; import java.io.*; import java.nio.file.*; import java.util.concurrent.TimeUnit; import java.util.concurrent.Executors; import java.util.concurrent.ExecutorService; import java.util.concurrent.Callable; import java.util.concurrent.Future; @Component public class WordToPdfConverter { private FileDownloader downloader; public WordToPdfConverter() { this.downloader = new FileDownloader(); } /** * 获取LibreOffice可执行文件路径 */ private String getLibreOfficeExecutable() { String os = System.getProperty("os.name").toLowerCase(); // 尝试多个可能的路径 if (os.contains("win")) { // Windows String[] possiblePaths = { "C:\\Program Files\\LibreOffice\\program\\soffice.exe", "C:\\Program Files (x86)\\LibreOffice\\program\\soffice.exe", "D:\\Program Files\\LibreOffice\\program\\soffice.exe", System.getenv("LIBREOFFICE_HOME") + "\\program\\soffice.exe" }; for (String path : possiblePaths) { if (path != null && new File(path).exists()) { return path; } } // 尝试在PATH中查找 return "soffice"; } else if (os.contains("mac")) { // macOS return "/Applications/LibreOffice.app/Contents/MacOS/soffice"; } else { // Linux/Unix return "libreoffice"; } } /** * 检查LibreOffice是否可用 */ public boolean checkLibreOffice() { try { String command = getLibreOfficeExecutable() + " --version"; ProcessBuilder pb = new ProcessBuilder(); if (System.getProperty("os.name").toLowerCase().contains("win")) { pb.command("cmd.exe", "/c", command); } else { pb.command("bash", "-c", command); } Process process = pb.start(); // 读取输出 StringBuilder output = new StringBuilder(); try (BufferedReader reader = new BufferedReader( new InputStreamReader(process.getInputStream()))) { String line; while ((line = reader.readLine()) != null) { output.append(line).append("\n"); } } int exitCode = process.waitFor(); if (exitCode == 0 && output.toString().contains("LibreOffice")) { System.out.println("✓ LibreOffice 检测成功"); System.out.println("版本信息: " + output.toString().trim()); return true; } } catch (Exception e) { System.err.println("✗ LibreOffice 检测失败: " + e.getMessage()); } System.err.println("请安装 LibreOffice:"); System.err.println("Windows: https://www.libreoffice.org/download/download/"); System.err.println("Mac: brew install libreoffice"); System.err.println("Linux: sudo apt-get install libreoffice"); return false; } /** * 使用LibreOffice转换Word到PDF */ public boolean convertWithLibreOffice(File inputFile, String outputPath) { if (!inputFile.exists() || inputFile.length() == 0) { System.err.println("输入文件不存在或为空"); return false; } try { // 准备输出目录 File outputFile = new File(outputPath); File outputDir = outputFile.getParentFile(); if (outputDir != null && !outputDir.exists()) { outputDir.mkdirs(); } String libreOfficePath = getLibreOfficeExecutable(); System.out.println("输入文件: " + inputFile.getAbsolutePath()); System.out.println("输出文件: " + outputPath); System.out.println("LibreOffice路径: " + libreOfficePath); // 构建命令 ProcessBuilder pb = new ProcessBuilder(); String[] command; if (System.getProperty("os.name").toLowerCase().contains("win")) { command = new String[] { "cmd.exe", "/c", "\"" + libreOfficePath + "\"", "--headless", "--convert-to", "pdf", "--outdir", outputDir.getAbsolutePath(), inputFile.getAbsolutePath() }; } else { command = new String[] { libreOfficePath, "--headless", "--convert-to", "pdf", "--outdir", outputDir.getAbsolutePath(), inputFile.getAbsolutePath() }; } pb.command(command); pb.redirectErrorStream(true); System.out.println("执行命令: " + String.join(" ", command)); Process process = pb.start(); // 异步读取输出 ExecutorService executor = Executors.newSingleThreadExecutor(); Future future = executor.submit(new StreamReader(process.getInputStream())); // 等待进程完成 boolean completed = process.waitFor(5, TimeUnit.MINUTES); if (!completed) { process.destroyForcibly(); System.err.println("转换超时"); return false; } int exitCode = process.exitValue(); String output = future.get(); executor.shutdown(); System.out.println("LibreOffice输出:\n" + output); System.out.println("退出代码: " + exitCode); if (exitCode == 0) { // 检查输出文件是否存在 String baseName = inputFile.getName(); String fileNameWithoutExt = baseName.substring(0, baseName.lastIndexOf('.')); String expectedPdf = outputDir.getAbsolutePath() + File.separator + fileNameWithoutExt + ".pdf"; File pdfFile = new File(expectedPdf); if (pdfFile.exists()) { // 重命名为指定名称(如果需要) if (!expectedPdf.equals(outputPath)) { Files.move(pdfFile.toPath(), outputFile.toPath(), StandardCopyOption.REPLACE_EXISTING); } System.out.println("✓ 转换成功: " + outputFile.getAbsolutePath()); System.out.println("文件大小: " + outputFile.length() + " 字节"); return true; } else { // 检查是否直接生成了指定名称的文件 if (outputFile.exists()) { System.out.println("✓ 转换成功: " + outputFile.getAbsolutePath()); return true; } else { System.err.println("✗ 输出文件未生成"); return false; } } } else { System.err.println("✗ LibreOffice转换失败"); return false; } } catch (Exception e) { System.err.println("转换过程异常: " + e.getMessage()); e.printStackTrace(); return false; } } /** * 异步读取进程输出 */ private static class StreamReader implements Callable { private InputStream inputStream; public StreamReader(InputStream inputStream) { this.inputStream = inputStream; } @Override public String call() throws Exception { StringBuilder output = new StringBuilder(); try (BufferedReader reader = new BufferedReader( new InputStreamReader(inputStream))) { String line; while ((line = reader.readLine()) != null) { output.append(line).append("\n"); } } return output.toString(); } } /** * 完整的URL到PDF转换流程 */ public boolean convertUrlToPdf(String wordUrl, String pdfOutputPath) { System.out.println("=== 开始Word到PDF转换 ==="); System.out.println("源URL: " + wordUrl); System.out.println("目标PDF: " + pdfOutputPath); try { // 1. 检查LibreOffice if (!checkLibreOffice()) { return false; } // 2. 下载Word文档 System.out.println("\n=== 下载Word文档 ==="); byte[] wordData = downloader.downloadFile(wordUrl); if (wordData == null || wordData.length == 0) { System.err.println("下载的文件内容为空"); return false; } // 3. 保存为临时文件 System.out.println("\n=== 准备临时文件 ==="); Path tempFile = Files.createTempFile("word_", ".docx"); Files.write(tempFile, wordData); System.out.println("临时文件: " + tempFile.toString()); // 4. 转换为PDF System.out.println("\n=== 开始转换 ==="); boolean success = convertWithLibreOffice(tempFile.toFile(), pdfOutputPath); // 5. 清理临时文件 System.out.println("\n=== 清理临时文件 ==="); try { Files.deleteIfExists(tempFile); System.out.println("临时文件已清理"); } catch (IOException e) { System.err.println("清理临时文件失败: " + e.getMessage()); } return success; } catch (Exception e) { System.err.println("\n✗ 转换失败: " + e.getMessage()); e.printStackTrace(); return false; } } /** * 批量转换 */ public void batchConvert(String[] urls, String outputDir) { System.out.println("=== 批量转换开始 ==="); System.out.println("文件数量: " + urls.length); System.out.println("输出目录: " + outputDir); File outputDirFile = new File(outputDir); if (!outputDirFile.exists()) { outputDirFile.mkdirs(); } int successCount = 0; int failCount = 0; for (int i = 0; i < urls.length; i++) { String url = urls[i]; String fileName = "document_" + (i + 1) + ".pdf"; String outputPath = outputDir + File.separator + fileName; System.out.println("\n--- 处理文件 " + (i + 1) + "/" + urls.length + " ---"); System.out.println("URL: " + url); boolean success = convertUrlToPdf(url, outputPath); if (success) { successCount++; System.out.println("✓ 转换成功: " + fileName); } else { failCount++; System.err.println("✗ 转换失败: " + fileName); } } System.out.println("\n=== 批量转换完成 ==="); System.out.println("成功: " + successCount); System.out.println("失败: " + failCount); System.out.println("输出目录: " + outputDir); } }