Files
contract-review/qingyun-service/src/main/java/com/qingyun/service/utils/PdfUtil.java
2026-01-30 14:25:12 +08:00

207 lines
7.8 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package com.qingyun.service.utils;
import cn.hutool.http.HttpUtil;
import com.documents4j.api.DocumentType;
import com.documents4j.api.IConverter;
import com.documents4j.job.LocalConverter;
import com.qingyun.common.config.QingYunConfig;
import com.qingyun.common.utils.file.FileUploadUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.ImageType;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
@Component
public class PdfUtil {
@Value("${server.port}")
private String port;
/**
* @param source 原文件
* @param desFilePath 生成图片的路径
* @param desFileName 生成图片的名称(多页文档时会变成:名称+下划线+从1开始的数字
* @return
*/
public Pair<Boolean, Object> pdfToImage(File source, String desFilePath, String desFileName) throws IOException {
//通过给定的源路径名字符串创建一个File实例
if (!source.exists()) {
return Pair.of(false, "文件不存在,无法转化");
}
//目录不存在则创建目录
File destination = new File(desFilePath);
if (!destination.exists()) {
boolean flag = destination.mkdirs();
System.out.println("创建文件夹结果:" + flag);
}
PDDocument doc = null;
try {
//加载PDF文件
doc = Loader.loadPDF(source);
PDFRenderer renderer = new PDFRenderer(doc);
//获取PDF文档的页数
int pageCount = doc.getNumberOfPages();
System.out.println("文档一共" + pageCount + "");
List<Object> fileList = new ArrayList<>();
for (int i = 0; i < pageCount; i++) {
// 只有一页的时候文件名为传入的文件名大于一页的文件名为文件名_自增加数字(从1开始)
String realFileName = pageCount > 1 ? desFileName + "_" + (i + 1) : desFileName;
// 每一页通过分辨率和颜色值进行转化
BufferedImage bufferedImage = renderer.renderImageWithDPI(i, 150, ImageType.RGB);
String relativePath = FileUploadUtils.getPathFileName(QingYunConfig.getUploadPath() + "/" + desFilePath, realFileName + "." + "png");
String filePath = QingYunConfig.getUploadPath() + "/" + desFilePath + "/" + realFileName + "." + "png";
// 确保目录存在
File imageFile = new File(filePath);
if (!imageFile.getParentFile().exists()) {
boolean mkdirs = imageFile.getParentFile().mkdirs(); // 创建父目录
if (!mkdirs) {
return Pair.of(false, "无法创建目录:" + imageFile.getParentFile().getAbsolutePath());
}
}
// 写入文件
ImageIO.write(bufferedImage, "png", imageFile);
// 文件名存入 list
fileList.add(relativePath);
}
return Pair.of(true, fileList);
} catch (IOException e) {
e.printStackTrace();
return Pair.of(false, "PDF转化图片异常");
} finally {
try {
if (doc != null) {
doc.close();
}
} catch (IOException e) {
System.out.println("关闭文档失败");
e.printStackTrace();
}
}
}
public File downloadFile(String pdfUrl, String name) throws IOException {
// name 去掉文件后缀
String newName = name.substring(0, name.lastIndexOf("."));
String projectUrl = QingYunConfig.getProjectUrl();
File tempFile = File.createTempFile(newName + System.currentTimeMillis(), getFileExtension(name));
long file = HttpUtil.downloadFile(projectUrl + pdfUrl, tempFile);
if (file <= 0) {
throw new IOException("无法下载 PDF 文件");
}
return tempFile;
}
public File convertWordToPdf(File wordFile) throws IOException, InterruptedException {
if (!wordFile.exists() || !wordFile.isFile()) {
throw new IOException("Word文件不存在或无效: " + wordFile.getAbsolutePath());
}
String osName = System.getProperty("os.name").toLowerCase();
if (osName.contains("win")) {
// Windows 系统使用 Apache POI 实现 Word 转 PDF
return convertWordToPdfWindows(wordFile);
} else {
// Linux 或其他系统使用 LibreOffice 实现 Word 转 PDF
return convertWordToPdfLinux(wordFile);
}
}
private File convertWordToPdfWindows(File wordFile) throws IOException {
File outputFile = new File(wordFile.getParent(),
FilenameUtils.getBaseName(wordFile.getName()) + ".pdf");
try {
IConverter converter = LocalConverter.builder().build();
Future<Boolean> conversion = converter
.convert(wordFile).as(DocumentType.MS_WORD)
.to(outputFile).as(DocumentType.PDF)
.prioritizeWith(1000) // optional
.schedule();
conversion.get(); // 这里会阻塞直到转换完成
return outputFile;
} catch (Exception e) {
throw new IOException("Word 转 PDF 失败: " + e.getMessage(), e);
}
}
private File convertWordToPdfLinux(File wordFile) throws IOException, InterruptedException {
// 保留现有的 Linux 实现逻辑
if (!isLibreOfficeInstalled()) {
throw new IOException("LibreOffice未安装! 请先执行: sudo apt-get install libreoffice");
}
File outputDir = wordFile.getParentFile();
String pdfName = FilenameUtils.getBaseName(wordFile.getName()) + ".pdf";
File outputFile = new File(outputDir, pdfName);
ProcessBuilder pb = new ProcessBuilder(
"/usr/bin/soffice",
"--headless",
"--convert-to", "pdf",
"--outdir", outputDir.getAbsolutePath(),
wordFile.getAbsolutePath()
);
pb.redirectErrorStream(true);
pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
Process process = pb.start();
boolean completed = process.waitFor(5, TimeUnit.MINUTES);
if (!completed) {
process.destroyForcibly();
throw new IOException("转换超时5分钟");
}
if (process.exitValue() != 0) {
throw new IOException("LibreOffice转换失败错误码: " + process.exitValue());
}
if (!outputFile.exists() || outputFile.length() == 0) {
throw new IOException("PDF文件未生成或为空");
}
return outputFile;
}
private boolean isLibreOfficeInstalled() {
try {
Process p = Runtime.getRuntime().exec("which soffice");
return p.waitFor(2, TimeUnit.SECONDS) && p.exitValue() == 0;
} catch (Exception e) {
return false;
}
}
/**
* 获取文件扩展名
* @param fileName 文件名
* @return 文件扩展名(包括点号)
*/
private String getFileExtension(String fileName) {
if (fileName == null || fileName.lastIndexOf('.') == -1) {
return ""; // 没有扩展名
}
return fileName.substring(fileName.lastIndexOf('.'));
}
}