当前位置: 首页 > news >正文

PDF或Word转图片(多线程+aspose+函数式接口)

PDF或Word转图片(多线程+aspose+函数式接口)

业务需求

有个业务需要把用户上传的PDF或者Word文件转成图片,用于前端展示

实现思路

这里直接用aspose实现即可,由于这种转换是非常慢的,所以还需要用到多线程

具体实现
引入aspose依赖
<!--aspose对应的word和pdf的依赖 -->
<dependency><groupId>com.aspose</groupId><artifactId>aspose-pdf</artifactId><version>17.3.0</version>
</dependency>
<dependency><groupId>com.aspose</groupId><artifactId>aspose-words</artifactId><version>24.1</version><classifier>jdk17</classifier>
</dependency><!--File类转成MultipartFile类需要的依赖 -->
<dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.3.20</version>
</dependency>
核心类
package com.smart.technology.utils;import com.aspose.pdf.*;
import com.aspose.pdf.devices.PngDevice;
import com.aspose.pdf.devices.Resolution;import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** PDF转图片工具类* 使用Aspose.PDF将PDF文档转换为图片* @author: daizhongliang* @date: 2025/10/28*/
public class PDFToImageUtils {/*** 将PDF文档转换为图片* @param inputPath  PDF文档的输入路径* @param outputDir  输出图片的目录* @return           转换后的图片文件列表* @throws Exception 如果转换过程中发生错误*/public static List<File> pdfToImg(String inputPath, String outputDir) throws Exception {List<File> imageFiles = new ArrayList<>();try {// 加载PDF文档Document pdfDocument = new Document(inputPath);System.out.println("文档页数: " + pdfDocument.getPages().size());// 创建输出目录File outputDirectory = new File(outputDir);if (!outputDirectory.exists()) {outputDirectory.mkdirs();}// 创建固定大小的线程池用于并行处理int processors = Runtime.getRuntime().availableProcessors();ExecutorService executor = Executors.newFixedThreadPool(processors);// 创建CompletableFuture列表用于并行处理每一页List<CompletableFuture<File>> futures = new ArrayList<>();// 并行处理每一页for (int i = 1; i <= pdfDocument.getPages().size(); i++) {final int pageIndex = i;CompletableFuture<File> future = CompletableFuture.supplyAsync(() -> {try {// 获取当前页Page page = pdfDocument.getPages().get_Item(pageIndex);// 设置图片保存选项Resolution resolution = new Resolution(300); // 设置分辨率PngDevice pngDevice = new PngDevice(resolution);// 设置渲染选项以提高图片质量RenderingOptions options = new RenderingOptions();options.setUseNewImagingEngine(true);pngDevice.setRenderingOptions(options);// 生成输出文件名String outputPath = outputDir + "page_" + pageIndex + ".png";// 保存为图片pngDevice.process(page, outputPath);File imageFile = new File(outputPath);System.out.println("已生成图片: " + outputPath);return imageFile;} catch (Exception e) {throw new RuntimeException("页面转换失败: " + e.getMessage(), e);}}, executor);futures.add(future);}// 等待所有任务完成CompletableFuture<Void> allFutures = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));allFutures.join();// 收集结果for (CompletableFuture<File> future : futures) {imageFiles.add(future.get());}// 关闭线程池executor.shutdown();return imageFiles;} catch (Exception e) {e.printStackTrace();throw e;}}/*** 测试main方法:将本地PDF文档转换为多张图片*/public static void main(String[] args) {try {// 输入PDF文件路径String inputPath = "D:\\test\\pdf\\sample.pdf";// 输出图片目录String outputDir = "D:\\test\\pdf\\output_images\\";System.out.println("开始转换PDF文档到图片...");System.out.println("输入文件: " + inputPath);System.out.println("输出目录: " + outputDir);// 执行转换List<File> imageFiles = pdfToImg(inputPath, outputDir);System.out.println("转换完成!");System.out.println("共生成 " + imageFiles.size() + " 张图片");} catch (Exception e) {System.err.println("转换失败: " + e.getMessage());e.printStackTrace();}}
}
package com.smart.technology.utils;import com.aspose.words.Document;
import com.aspose.words.ImageSaveOptions;
import com.aspose.words.PageSet;
import com.aspose.words.SaveFormat;import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** Word转图片工具类* 使用Aspose.Words将Word文档转换为图片* @author: daizhongliang* @date: 2025/10/28*/
public class WordToImageUtils {/*** 将Word文档转换为图片* @param inputPath  Word文档的输入路径* @param outputDir  输出图片的目录* @return           转换后的图片文件列表* @throws Exception 如果转换过程中发生错误*/public static List<File> wordToImg(String inputPath, String outputDir) throws Exception {List<File> imageFiles = new ArrayList<>();try {// 加载Word文档Document doc = new Document(inputPath);System.out.println("文档页数: " + doc.getPageCount());// 创建输出目录File outputDirectory = new File(outputDir);if (!outputDirectory.exists()) {outputDirectory.mkdirs();}// 创建固定大小的线程池用于并行处理int processors = Runtime.getRuntime().availableProcessors();ExecutorService executor = Executors.newFixedThreadPool(processors);// 创建CompletableFuture列表用于并行处理每一页List<CompletableFuture<File>> futures = new ArrayList<>();// 并行处理每一页for (int i = 0; i < doc.getPageCount(); i++) {final int pageIndex = i;CompletableFuture<File> future = CompletableFuture.supplyAsync(() -> {try {ImageSaveOptions options = new ImageSaveOptions(SaveFormat.PNG);options.setPrettyFormat(true);options.setUseAntiAliasing(true);options.setUseHighQualityRendering(true);options.setPageSet(new PageSet(pageIndex)); // 设置当前页// 生成输出文件名String outputPath = outputDir + "page_" + (pageIndex + 1) + ".png";doc.save(outputPath, options);File imageFile = new File(outputPath);System.out.println("已生成图片: " + outputPath);return imageFile;} catch (Exception e) {throw new RuntimeException("页面转换失败: " + e.getMessage(), e);}}, executor);futures.add(future);}// 等待所有任务完成CompletableFuture<Void> allFutures = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));allFutures.join();// 收集结果for (CompletableFuture<File> future : futures) {imageFiles.add(future.get());}// 关闭线程池executor.shutdown();return imageFiles;} catch (Exception e) {e.printStackTrace();throw e;}}/*** 测试main方法:将本地Word文档转换为多张图片*/public static void main(String[] args) {try {// 输入Word文件路径String inputPath = "D:\\test\\1.docx";// 输出图片目录String outputDir = "D:\\test\\output_images\\";System.out.println("开始转换Word文档到图片...");System.out.println("输入文件: " + inputPath);System.out.println("输出目录: " + outputDir);// 执行转换List<File> imageFiles = wordToImg(inputPath, outputDir);System.out.println("转换完成!");System.out.println("共生成 " + imageFiles.size() + " 张图片");} catch (Exception e) {System.err.println("转换失败: " + e.getMessage());e.printStackTrace();}}
}
线程池配置类
package com.smart.technology.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;/*** 异步线程池配置类* 用于优化文件转换等耗时操作的性能*/
@Configuration
@EnableAsync
public class AsyncThreadPoolConfig {/*** 文件转换专用线程池* 核心线程数: 10* 最大线程数: 20* 队列容量: 100* 线程空闲时间: 60秒* 拒绝策略: 调用者运行策略** @return Executor 线程池执行器*/@Bean("fileConvertTaskExecutor")public Executor fileConvertTaskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();int processors = Runtime.getRuntime().availableProcessors();// 核心线程数executor.setCorePoolSize(processors);// 最大线程数executor.setMaxPoolSize(processors * 2);// 队列容量executor.setQueueCapacity(100);// 线程空闲时间executor.setKeepAliveSeconds(60);// 线程名称前缀executor.setThreadNamePrefix("file-convert-");// 拒绝策略: 当线程池达到最大容量且队列已满时,新任务将在调用者线程中执行executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());// 初始化线程池executor.initialize();return executor;}
}
业务层具体引用
@Autowired
private Executor fileConvertTaskExecutor;/*** 将Word/PDF文件转换为图片并上传* @param file 上传的文件* @param packageId 课程包ID* @return 转换结果,包含原始文件URL和图片列表*/
@Override
public Result<FileImagesUploadDTO> uploadWord2Img(MultipartFile file, Long packageId) {// 验证参数if (file == null || file.isEmpty()) {return Result.fail("文件不能为空");}if (packageId == null) {return Result.fail("课程包ID不能为空");}// 获取文件后缀String originalFilename = file.getOriginalFilename();if (originalFilename == null) {return Result.fail("文件名不能为空");}// 获取文件扩展名String fileExtension = originalFilename.substring(originalFilename.lastIndexOf("."));// word或者pdf目录名称String dirName = "";// word或者pdf转换后的图片目录名称String dirImagesName = "";if (fileExtension.contains(".doc") || fileExtension.contains(".docx")) {dirName = "Word";dirImagesName = "WordImage";} else if (fileExtension.contains(".pdf")) {dirName = "PDF";dirImagesName = "PDFImage";} else {return Result.fail("不支持的文件类型");}String fileUUID = String.valueOf(UUID.randomUUID());// 生成唯一文件名String fileName = fileUUID + fileExtension;// 构建文件路径String filePath = String.format("/Course/default/%d/%s/", packageId, dirName);// 1、上传word或pdf文件到COS云存储FileUploadDTO fileInfo = cosService.uploadFile(file, filePath, fileName);try {// 2、根据文件类型调用对应的转换方法List<String> imageUrls;if ("Word".equals(dirName)) {imageUrls = convertAndUploadImages(file, packageId, fileUUID, dirName, dirImagesName, (tempFilePath, tempImagePath) -> WordToImageUtils.wordToImg(tempFilePath, tempImagePath));log.info("Word文件转换为图片并上传完成,共{}张图片", imageUrls.size());} else if ("PDF".equals(dirName)) {imageUrls = convertAndUploadImages(file, packageId, fileUUID, dirName, dirImagesName, (tempFilePath, tempImagePath) -> PDFToImageUtils.pdfToImg(tempFilePath, tempImagePath));log.info("PDF文件转换为图片并上传完成,共{}张图片", imageUrls.size());} else {return Result.fail("不支持的文件类型");}// 创建返回对象FileImagesUploadDTO result = new FileImagesUploadDTO();result.setFileUrl(fileInfo.getFilePath());result.setFileImages(imageUrls);return Result.success(result);} catch (Exception e) {throw new RuntimeException("文件转换为图片失败: " + e.getMessage(), e);}
}/*** 函数接口,用于定义文件转图片的转换方法*/
@FunctionalInterface
private interface ImageConverter {List<File> convert(String sourcePath, String targetPath) throws Exception;
}/*** 通用的文件转换和图片上传方法* @param file 原始文件* @param packageId 课程包ID* @param fileUUID 文件UUID* @param dirName 目录名称* @param dirImagesName 图片目录名称* @param converter 转换接口实现* @return 图片URL列表* @throws Exception 转换或上传过程中的异常*/
private List<String> convertAndUploadImages(MultipartFile file, Long packageId, String fileUUID, String dirName, String dirImagesName, ImageConverter converter) throws Exception {// 创建临时目录String tempDir = System.getProperty("java.io.tmpdir") + dirName + "ToImages" + File.separator + System.currentTimeMillis();File tempDirFile = new File(tempDir);tempDirFile.mkdirs();try {// 将上传的文件保存到临时路径String tempFilePath = tempDir + File.separator + file.getOriginalFilename();try (FileOutputStream fos = new FileOutputStream(tempFilePath)) {IOUtils.copy(file.getInputStream(), fos);}// 临时存储转换后图片的路径String tempImagePath = tempDir + File.separator + dirImagesName;// 调用转换器将文件转换为图片(异步并行处理)List<File> imageFiles = convertImagesAsync(tempFilePath, tempImagePath, converter);// 上传转换后的图片到COS(异步并行上传)List<String> imageUrls = uploadImagesAsync(imageFiles, packageId, fileUUID, dirImagesName);return imageUrls;} finally {// 清理临时目录tempDirFile.delete();}
}/*** 异步并行转换文件为图片* @param sourcePath 源文件路径* @param targetPath 目标图片路径* @param converter 转换器* @return 图片文件列表* @throws Exception 转换过程中的异常*/
private List<File> convertImagesAsync(String sourcePath, String targetPath, ImageConverter converter) throws Exception {// 调用转换器将文件转换为图片return converter.convert(sourcePath, targetPath);
}/*** 异步并行上传图片到COS* @param imageFiles 图片文件列表* @param packageId 课程包ID* @param fileUUID 文件UUID* @param dirImagesName 图片目录名称* @return 图片URL列表* @throws Exception 上传过程中的异常*/
private List<String> uploadImagesAsync(List<File> imageFiles, Long packageId, String fileUUID, String dirImagesName) throws Exception {// 上传转换后的图片到COSString imageDirPath = String.format("/Course/default/%d/%s/%s", packageId, dirImagesName, fileUUID);List<String> imageUrls = new ArrayList<>();// 创建CompletableFuture列表用于并行上传List<CompletableFuture<FileUploadDTO>> futures = new ArrayList<>();// 遍历图片文件列表,异步上传到COSint imageIndex = 1;for (File imageFile : imageFiles) {final int index = imageIndex;CompletableFuture<FileUploadDTO> future = CompletableFuture.supplyAsync(() -> {try {// 生成自定义文件名:uuid_1格式String customFileName = UUID.randomUUID() + "_" + index + ".png";// 将File对象转换为MultipartFileMultipartFile multipartImageFile = new MockMultipartFile(customFileName,customFileName,"image/png",Files.newInputStream(imageFile.toPath()));// 上传到COS,使用指定的文件名return cosService.uploadFile(multipartImageFile, imageDirPath, customFileName);} catch (Exception e) {log.error("图片上传失败: {}", imageFile.getName(), e);throw new RuntimeException("图片上传失败: " + e.getMessage(), e);}}, fileConvertTaskExecutor);futures.add(future);imageIndex++;}// 等待所有上传任务完成CompletableFuture<Void> allFutures = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));allFutures.join();// 收集上传结果for (CompletableFuture<FileUploadDTO> future : futures) {FileUploadDTO imageInfo = future.get();imageUrls.add(imageInfo.getFilePath());log.info("图片上传成功: {}, URL: {}", imageInfo.getFileName(), imageInfo.getFilePath());}return imageUrls;
}

骚戴解析:上面的逻辑其实很简单,首先把用户上传的pdf或者word上传到腾讯云cos云存储,然后把上传的pdf或者word文件复制到一个临时目录,再临时目录建立一个子目录用于存储转换后的图片,调用上面核心类的工具类去转换pdf或者word,把转换的List<File>转成MultipartFile然后调用腾讯云cos云存储的上传接口把转换后的图片上传到腾讯云cos云存储的桶上面去,最后把临时文件目录给删除掉。中间用了高级的东西,函数式接口来解耦和复用,因为pdf和word的整体逻辑其实差不多,只是调用的工具类不一样同时上传后的目录不一样而已

http://www.dtcms.com/a/568693.html

相关文章:

  • .docx 和 .doc 是 Microsoft Word 文档的两种主要文件格式
  • RabbitMQ 实战:理解“不公平分发(Unfair Dispatching)”机制
  • 前端缓存技术和使用场景
  • 网站建设价格请咨询兴田德润个人网站建设简历
  • 虚拟机导入报错:行 25: 硬件系列“vmx-21”不受支持。
  • C# TCP 服务器和客户端
  • 【R语言】构建GO、KEGG相关不同物种的R包
  • 缓存三部曲:从线程到分布式
  • LS67211_VC1:48KHz低延时AI降噪USB直播麦克风音频处理器
  • 【C++】分治-快速排序算法习题
  • MySQL第四次作业(索引、视图)
  • Partial Prompt Templates in LangChain
  • 泉州网站平台建设公司网站建设素材图
  • 计算机技术员网站建设怎么网站底部 设计
  • 第50届ICPC亚洲区域赛·成都站,非凸科技持续护航顶尖赛事
  • 企业微信自建应用开发详细教程,如何获取授权链接?如何使用js-sdk?
  • html css js网页制作成品——高定晚礼服HTML+CSS网页设计(5页)附源码
  • 蓝牙钥匙 第43次 特殊用户群体场景下的汽车数字钥匙系统:包容性设计与技术创新
  • 万网如何建设购物网站wordpress分类目录 菜单 页面
  • 智能网联汽车 HD map架构解析
  • HTML常用单标签速查手册
  • 告别算法死记硬背,Hello-Algo 让抽象知识变直观,搭配cpolar穿透工具更自由
  • Go从入门到精通(27) - 并行任务处理器
  • Claude Code 使用 MiniMax M2 模型
  • Auto CAD二次开发——复制和旋转图形对象
  • 全屏响应式网站模板网站seo综合公司
  • php做简单网站教程视频教程企业门户网站模板 下载
  • Rust开发实战之WebSocket通信实现(tokio-tungstenite)
  • 编译缓存利器 ccahce、sccahce
  • Rust开发实战之使用 Reqwest 实现 HTTP 客户端请求