多線程完成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格式編碼則不需要前端進行額外的轉換。