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<String> 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<String> {
|
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);
|
}
|
}
|