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 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 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 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('.')); } }