Java使用多线程完成PDF文件转图片

    技术2023-07-29  85

    多线程完成PDF文件转图片

    系统需要将PDF文件由后台直接转为img图片,供前端页面直接展示,不需要用户下载即可预览文件内容。直接转换时如果文件过大,耗时很长,影响用户体验,后调研后使用多线程方式进行,显著加快图片转换速度。 原始版访问:https://blog.csdn.net/wmf_helloWorld/article/details/104051137

    1、创建线程池

    ExecutorService executorService = new ThreadPoolExecutor(5, 10, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(50));

    初始化线程池,因系统默认一次预览5张图片,进行分页分步预览,所以设置核心线程池大小为5.设置最后队列为new ArrayBlockingQueue(50)),可以排队50个等待任务,若大于50则不建议预览,或根据需要使用其他队列。 可访问:https://www.cnblogs.com/dafanjoy/p/9729358.html 2、主要转换方法体 2.1、全部转换

    public List<String> pdfTurnImage(String filePath) throws CodeException { List<String> fileImageList; File file = new File(filePath); try(PDDocument pdDocument = PDDocument.load(file)) { PdfReader reader = new PdfReader(filePath); int pages = reader.getNumberOfPages(); LOGGER.info(LogType.INFO, "pdf文件共有"+ pages +"页文件"); String[] imgStrArr = new String[pages]; fileImageList = Arrays.asList(imgStrArr); List<Future<Boolean>> futureTaskList =new ArrayList<>(); for(int i=0; i<pages; i++) { LOGGER.info(LogType.INFO, "读取转换第"+ (i+1) +"页文件"); PdfPreviewTask task = new PdfPreviewTask(fileImageList, i, i, filePath); futureTaskList.add(SystemEnvironment.executorService.submit(task)); } for (Future<Boolean> future : futureTaskList) { if(future.get()) { future.cancel(true); } } } catch (Exception e) { LOGGER.error(LogType.ERROR, e); throw new CodeException(XYRFileServerConstants.ERROR_CODE_2017, XYRFileServerConstants.ERROR_MESSAGE_2017, e); } return fileImageList; }

    filePath:文件路径 List<Future> futureTaskList:list集合存放后续创建的线程,并且使用Future获取小程返回结果,下方for循环保证文件全部转换成功后关闭线程,并执行结束。(Boolean为之后多线程PdfPreviewTask的返回值,可根据需要进行修改)。 fileImageList :定义固定大小的集合,之后将每个图片放在集合的固定位置,防止转换之后图片放置顺序错误。

    2.2,分页转换

    public List<String> getFileContent(int start, int numbers, String filePath) throws CodeException { List<String> fileImageList; File file = new File(filePath); int begin = start-1; try(PDDocument pdDocument = PDDocument.load(file)) { PdfReader reader = new PdfReader(filePath); int pages = reader.getNumberOfPages(); LOGGER.info(LogType.INFO, "pdf文件共有"+ pages +"页文件"); String[] imgStrArr = new String[numbers]; fileImageList = Arrays.asList(imgStrArr); List<Future<Boolean>> futureTaskList =new ArrayList<>(); for(int i=0; i<numbers; i++) { LOGGER.info(LogType.INFO, "读取转换第"+ begin +"页文件"); PdfPreviewTask task = new PdfPreviewTask(fileImageList, begin, i, filePath); begin++; futureTaskList.add(SystemEnvironment.executorService.submit(task)); } for (Future<Boolean> future : futureTaskList) { if(future.get()) { future.cancel(true); } } } catch (Exception e) { LOGGER.error(LogType.ERROR, e); throw new CodeException(XYRFileServerConstants.ERROR_CODE_2017, XYRFileServerConstants.ERROR_MESSAGE_2017, e); } return fileImageList; }

    start:开始页码 numbers:分页转换页数 filePath:文件路径

    3、文件预览线程

    /** * <pre> * Modify Information:pdf文件预览 * Author Date Description * ============ ============= ============================ * wumf 2020年7月1日 create this file * </pre> */ public class PdfPreviewTask implements Callable<Boolean>{ private static Loggerx logger = Loggerx.getLogger("system"); /** * 图片列表 */ private List<String> imageList; /** * 图片存放集合位置 */ private int listPosition; /** * 页码 */ private int pageNumbers; /** * 文件路径 */ private String filePath; public PdfPreviewTask(List<String> imageList, int pageNumbers, int listPosition, String filePath) { this.imageList = imageList; this.listPosition = listPosition; this.pageNumbers = pageNumbers; this.filePath = filePath; } @Override public Boolean call() throws Exception { File file = new File(filePath); try(PDDocument pdDocument = PDDocument.load(file)) { PDFRenderer renderer = new PDFRenderer(pdDocument); logger.info(LogType.INFO, TimeUtil.getTimeStamp()+"线程开始执行,转换第"+pageNumbers+"页文件"); BufferedImage image = renderer.renderImageWithDPI(pageNumbers, Integer.parseInt(SystemEnvironment.dpi)); String imageBase64 = base64Image(image); if (StringUtil.isNotEmpty(imageBase64)) { if (!imageBase64.startsWith("data:image")) { imageBase64 = "data:image/jpeg;base64," + imageBase64; } imageList.set(listPosition, imageBase64); } logger.info(LogType.INFO, TimeUtil.getTimeStamp()+"线程执行结束,转换第"+pageNumbers+"页文件"); return true; } catch (Exception e) { logger.error(LogType.ERROR, "线程执行异常结束"+e.getMessage(), e); } return false; } private static String base64Image(BufferedImage image) throws CodeException { try(ByteArrayOutputStream baos = new ByteArrayOutputStream()) { ImageIO.write(image, "png", baos);//写入流中 byte[] bytes = baos.toByteArray();//转换成字节 return new String(Base64.encode(bytes),"UTF-8"); } catch (Exception e) { throw new CodeException(XYRFileServerConstants.ERROR_CODE_2017, XYRFileServerConstants.ERROR_MESSAGE_2017, e); } }

    通过初始化线程时的构造方法参数,设置所需的内容。 renderer.renderImageWithDPI(pageNumbers, Integer.parseInt(SystemEnvironment.dpi));为主要转换方法。具体参数百度。转换为base64格式编码则不需要前端进行额外的转换。

    Processed: 0.010, SQL: 9