From bb0139bee6239b66af884850d9ba561e38c3711b Mon Sep 17 00:00:00 2001 From: gaoxiongzaq Date: Mon, 20 May 2024 10:12:11 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9EPDF=E7=BA=BF=E7=A8=8B?= =?UTF-8?q?=E7=AE=A1=E7=90=86=EF=BC=8C=E8=B6=85=E6=97=B6=E7=AE=A1=E7=90=86?= =?UTF-8?q?=EF=BC=8C=E5=86=85=E5=AD=98=E7=BC=93=E5=AD=98=E7=AE=A1=E7=90=86?= =?UTF-8?q?=EF=BC=8C=E6=9B=B4=E6=96=B0PDF=E8=A7=A3=E6=9E=90=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- server/src/main/config/application.properties | 8 ++ .../cn/keking/config/ConfigConstants.java | 67 ++++++++++ .../keking/config/ConfigRefreshComponent.java | 12 ++ .../cn/keking/service/FileHandlerService.java | 122 ++++++++++-------- .../service/cache/NotResourceCache.java | 25 +++- 6 files changed, 178 insertions(+), 58 deletions(-) diff --git a/pom.xml b/pom.xml index daade795..bceae1d8 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ 2.7.7 1.4.2 5.17.2 - 2.0.29 + 3.0.2 1.4.0 3.0.4 0.2.1 diff --git a/server/src/main/config/application.properties b/server/src/main/config/application.properties index 7de75bc8..5b8e5874 100644 --- a/server/src/main/config/application.properties +++ b/server/src/main/config/application.properties @@ -110,6 +110,14 @@ convertMedias = ${KK_CONVERTMEDIAS:avi,mov,wmv,mkv,3gp,rm} #PDF预览模块设置 #配置PDF文件生成图片的像素大小,dpi 越高,图片质量越清晰,同时也会消耗更多的计算资源。 pdf2jpg.dpi = ${KK_PDF2JPG_DPI:144} +#PDF转换超时设置 (低于50页) 温馨提示这里数字仅供参考 +pdf.timeout =${KK_pdf_TIMEOUT:90} +#PDF转换超时设置 (高于50小于200页) +pdf.timeout80 =${KK_PDF_TIMEOUT80:180} +#PDF转换超时设置 (大于200页) +pdf.timeout200 =${KK_PDF_TIMEOUT200:300} +#PDF转换线程设置 +pdf.thread =${KK_PDF_THREAD:5} #是否禁止演示模式 pdf.presentationMode.disable = ${KK_PDF_PRESENTATION_MODE_DISABLE:true} #是否禁止打开文件 diff --git a/server/src/main/java/cn/keking/config/ConfigConstants.java b/server/src/main/java/cn/keking/config/ConfigConstants.java index 19251cd2..432ea2dc 100644 --- a/server/src/main/java/cn/keking/config/ConfigConstants.java +++ b/server/src/main/java/cn/keking/config/ConfigConstants.java @@ -67,6 +67,10 @@ public class ConfigConstants { private static String homePagination; private static String homePageSize; private static String homeSearch; + private static int pdfTimeout; + private static int pdfTimeout80; + private static int pdfTimeout200; + private static int pdfThread; public static final String DEFAULT_CACHE_ENABLED = "true"; public static final String DEFAULT_TXT_TYPE = "txt,html,htm,asp,jsp,xml,json,properties,md,gitignore,log,java,py,c,cpp,sql,sh,bat,m,bas,prg,cmd,xbrl"; @@ -107,6 +111,10 @@ public class ConfigConstants { public static final String DEFAULT_HOME_PAGINATION = "true"; public static final String DEFAULT_HOME_PAGSIZE = "15"; public static final String DEFAULT_HOME_SEARCH = "true"; + public static final String DEFAULT_PDF_TIMEOUT = "90"; + public static final String DEFAULT_PDF_TIMEOUT80 = "180"; + public static final String DEFAULT_PDF_TIMEOUT200 = "300"; + public static final String DEFAULT_PDF_THREAD = "5"; public static Boolean isCacheEnabled() { return cacheEnabled; @@ -580,6 +588,65 @@ public class ConfigConstants { ConfigConstants.cadThread = cadThread; } + /** + * 以下为pdf转换模块设置 + */ + public static int getPdfTimeout() { + return pdfTimeout; + } + + @Value("${pdf.timeout:90}") + public void setPdfTimeout(int pdfTimeout) { + setPdfTimeoutValue(pdfTimeout); + } + + public static void setPdfTimeoutValue(int pdfTimeout) { + ConfigConstants.pdfTimeout = pdfTimeout; + } + + + public static int getPdfTimeout80() { + return pdfTimeout80; + } + + @Value("${pdf.timeout80:180}") + public void setPdfTimeout80(int pdfTimeout80) { + setPdfTimeout80Value(pdfTimeout80); + } + + public static void setPdfTimeout80Value(int pdfTimeout80) { + ConfigConstants.pdfTimeout80 = pdfTimeout80; + } + + + + public static int getPdfTimeout200() { + return pdfTimeout200; + } + + @Value("${pdf.timeout200:300}") + public void setPdfTimeout200(int pdfTimeout200) { + setPdfTimeout200Value(pdfTimeout200); + } + + public static void setPdfTimeout200Value(int pdfTimeout200) { + ConfigConstants.pdfTimeout200 = pdfTimeout200; + } + + + public static int getPdfThread() { + return pdfThread; + } + + @Value("${pdf.thread:5}") + public void setPdfThread(int pdfThread) { + setPdfThreadValue(pdfThread); + } + + public static void setPdfThreadValue(int pdfThread) { + ConfigConstants.pdfThread = pdfThread; + } + /** * 以下为OFFICE转换模块设置 */ diff --git a/server/src/main/java/cn/keking/config/ConfigRefreshComponent.java b/server/src/main/java/cn/keking/config/ConfigRefreshComponent.java index 6ac3d85c..3482c514 100644 --- a/server/src/main/java/cn/keking/config/ConfigRefreshComponent.java +++ b/server/src/main/java/cn/keking/config/ConfigRefreshComponent.java @@ -78,6 +78,10 @@ public class ConfigRefreshComponent { String homePagination; String homePageSize; String homeSearch; + int pdfTimeout; + int pdfTimeout80; + int pdfTimeout200; + int pdfThread; while (true) { FileReader fileReader = new FileReader(configFilePath); BufferedReader bufferedReader = new BufferedReader(fileReader); @@ -126,6 +130,10 @@ public class ConfigRefreshComponent { homePageSize = properties.getProperty("home.pagesize", ConfigConstants.DEFAULT_HOME_PAGSIZE); homeSearch = properties.getProperty("home.search", ConfigConstants.DEFAULT_HOME_SEARCH); cadThread = Integer.parseInt(properties.getProperty("cad.thread", ConfigConstants.DEFAULT_CAD_THREAD)); + pdfTimeout = Integer.parseInt(properties.getProperty("pdf.timeout", ConfigConstants.DEFAULT_PDF_TIMEOUT)); + pdfTimeout80 = Integer.parseInt(properties.getProperty("pdf.timeout80", ConfigConstants.DEFAULT_PDF_TIMEOUT80)); + pdfTimeout200 = Integer.parseInt(properties.getProperty("pdf.timeout200", ConfigConstants.DEFAULT_PDF_TIMEOUT200)); + pdfThread = Integer.parseInt(properties.getProperty("pdf.thread", ConfigConstants.DEFAULT_PDF_THREAD)); prohibitArray = prohibit.split(","); ConfigConstants.setCacheEnabledValueValue(cacheEnabled); @@ -169,6 +177,10 @@ public class ConfigRefreshComponent { ConfigConstants.setHomePaginationValue(homePagination); ConfigConstants.setHomePageSizeValue(homePageSize); ConfigConstants.setHomeSearchValue(homeSearch); + ConfigConstants.setPdfTimeoutValue(pdfTimeout); + ConfigConstants.setPdfTimeout80Value(pdfTimeout80); + ConfigConstants.setPdfTimeout200Value(pdfTimeout200); + ConfigConstants.setPdfThreadValue(pdfThread); setWatermarkConfig(properties); bufferedReader.close(); fileReader.close(); diff --git a/server/src/main/java/cn/keking/service/FileHandlerService.java b/server/src/main/java/cn/keking/service/FileHandlerService.java index 4aa8b56a..7ef3f568 100644 --- a/server/src/main/java/cn/keking/service/FileHandlerService.java +++ b/server/src/main/java/cn/keking/service/FileHandlerService.java @@ -14,8 +14,8 @@ import com.aspose.cad.*; import com.aspose.cad.fileformats.cad.CadDrawTypeMode; import com.aspose.cad.fileformats.tiff.enums.TiffExpectedFormat; import com.aspose.cad.imageoptions.*; -import com.itextpdf.text.pdf.PdfReader; import org.apache.commons.lang3.exception.ExceptionUtils; +import org.apache.pdfbox.Loader; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.rendering.ImageType; import org.apache.pdfbox.rendering.PDFRenderer; @@ -37,7 +37,10 @@ import java.io.*; import java.net.URLDecoder; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; import java.util.concurrent.*; import java.util.stream.IntStream; @@ -236,9 +239,9 @@ public class FileHandlerService implements InitializingBean { boolean forceUpdatedCache = fileAttribute.forceUpdatedCache(); boolean usePasswordCache = fileAttribute.getUsePasswordCache(); String filePassword = fileAttribute.getFilePassword(); - String pdfPassword = null; - PDDocument doc = null; - PdfReader pdfReader = null; + PDDocument doc; + final String[] pdfPassword = {null}; + final int[] pageCount = new int[1]; if (!forceUpdatedCache) { List cacheResult = this.loadPdf2jpgCache(pdfFilePath); if (!CollectionUtils.isEmpty(cacheResult)) { @@ -246,64 +249,77 @@ public class FileHandlerService implements InitializingBean { } } List imageUrls = new ArrayList<>(); + File pdfFile = new File(fileNameFilePath); + if (!pdfFile.exists()) { + return null; + } + int index = pdfFilePath.lastIndexOf("."); + String folder = pdfFilePath.substring(0, index); + File path = new File(folder); + if (!path.exists() && !path.mkdirs()) { + logger.error("创建转换文件【{}】目录失败,请检查目录权限!", folder); + } try { - File pdfFile = new File(fileNameFilePath); - if (!pdfFile.exists()) { - return null; - } - doc = PDDocument.load(pdfFile, filePassword); + doc = Loader.loadPDF(pdfFile, filePassword); doc.setResourceCache(new NotResourceCache()); - int pageCount = doc.getNumberOfPages(); - PDFRenderer pdfRenderer = new PDFRenderer(doc); - int index = pdfFilePath.lastIndexOf("."); - String folder = pdfFilePath.substring(0, index); - File path = new File(folder); - if (!path.exists() && !path.mkdirs()) { - logger.error("创建转换文件【{}】目录失败,请检查目录权限!", folder); + pageCount[0] = doc.getNumberOfPages(); + } catch (IOException e) { + Throwable[] throwableArray = ExceptionUtils.getThrowables(e); + for (Throwable throwable : throwableArray) { + if (throwable instanceof IOException || throwable instanceof EncryptedDocumentException) { + if (e.getMessage().toLowerCase().contains(PDF_PASSWORD_MSG)) { + pdfPassword[0] = PDF_PASSWORD_MSG; //查询到该文件是密码文件 输出带密码的值 + } + } } - String imageFilePath; - for (int pageIndex = 0; pageIndex < pageCount; pageIndex++) { - imageFilePath = folder + File.separator + pageIndex + PDF2JPG_IMAGE_FORMAT; - BufferedImage image = pdfRenderer.renderImageWithDPI(pageIndex, ConfigConstants.getPdf2JpgDpi(), ImageType.RGB); - ImageIOUtil.writeImage(image, imageFilePath, ConfigConstants.getPdf2JpgDpi()); - String imageUrl = this.getPdf2jpgUrl(pdfFilePath, pageIndex); - imageUrls.add(imageUrl); + if (!PDF_PASSWORD_MSG.equals(pdfPassword[0])) { //该文件异常 错误原因非密码原因输出错误 + logger.error("Convert pdf exception, pdfFilePath:{}", pdfFilePath, e); } + throw new Exception(e); + } + Callable > call = () -> { try { - if (!ObjectUtils.isEmpty(filePassword)) { //获取到密码 判断是否是加密文件 - pdfReader = new PdfReader(fileNameFilePath); //读取PDF文件 通过异常获取该文件是否有密码字符 + String imageFilePath; + BufferedImage image = null; + PDFRenderer pdfRenderer = new PDFRenderer(doc); + pdfRenderer.setSubsamplingAllowed(true); + for (int pageIndex = 0; pageIndex < pageCount[0]; pageIndex++) { + imageFilePath = folder + File.separator + pageIndex + PDF2JPG_IMAGE_FORMAT; + image = pdfRenderer.renderImageWithDPI(pageIndex, ConfigConstants.getPdf2JpgDpi(), ImageType.RGB); + ImageIOUtil.writeImage(image, imageFilePath, ConfigConstants.getPdf2JpgDpi()); + String imageUrl = this.getPdf2jpgUrl(pdfFilePath, pageIndex); + imageUrls.add(imageUrl); } - } catch (Exception e) { //获取异常方法 判断是否有加密字符串 - Throwable[] throwableArray = ExceptionUtils.getThrowables(e); - for (Throwable throwable : throwableArray) { - if (throwable instanceof IOException || throwable instanceof EncryptedDocumentException) { - if (e.getMessage().toLowerCase().contains(PDF_PASSWORD_MSG)) { - pdfPassword = PDF_PASSWORD_MSG; //查询到该文件是密码文件 输出带密码的值 - } - } - } - if (!PDF_PASSWORD_MSG.equals(pdfPassword)) { //该文件异常 错误原因非密码原因输出错误 - logger.error("Convert pdf exception, pdfFilePath:{}", pdfFilePath, e); - } - + image.flush(); + } catch (IOException e) { + throw new Exception(e); } finally { - if (pdfReader != null) { //关闭 - pdfReader.close(); - } - } - - if (usePasswordCache || !PDF_PASSWORD_MSG.equals(pdfPassword)) { //加密文件 判断是否启用缓存命令 - this.addPdf2jpgCache(pdfFilePath, pageCount); - } - } catch (IOException e) { - if (!e.getMessage().contains(PDF_PASSWORD_MSG)) { - logger.error("Convert pdf to jpg exception, pdfFilePath:{}", pdfFilePath, e); + doc.close(); } + return imageUrls; + }; + Future> result = pool.submit(call); + int pdftimeout; + if(pageCount[0] <=50){ + pdftimeout = ConfigConstants.getPdfTimeout(); + }else if(pageCount[0] <=200){ + pdftimeout = ConfigConstants.getPdfTimeout80(); + }else { + pdftimeout = ConfigConstants.getPdfTimeout200(); + } + try { + result.get(pdftimeout, TimeUnit.SECONDS); + // 如果在超时时间内,没有数据返回:则抛出TimeoutException异常 + } catch (InterruptedException | ExecutionException e) { throw new Exception(e); + } catch (TimeoutException e) { + throw new Exception("overtime"); } finally { - if (doc != null) { //关闭 - doc.close(); - } + //关闭 + doc.close(); + } + if (usePasswordCache || ObjectUtils.isEmpty(filePassword)) { //加密文件 判断是否启用缓存命令 + this.addPdf2jpgCache(pdfFilePath, pageCount[0]); } return imageUrls; } diff --git a/server/src/main/java/cn/keking/service/cache/NotResourceCache.java b/server/src/main/java/cn/keking/service/cache/NotResourceCache.java index 1fd70918..46ec6f0f 100644 --- a/server/src/main/java/cn/keking/service/cache/NotResourceCache.java +++ b/server/src/main/java/cn/keking/service/cache/NotResourceCache.java @@ -2,9 +2,12 @@ package cn.keking.service.cache; import org.apache.pdfbox.cos.COSObject; import org.apache.pdfbox.pdmodel.DefaultResourceCache; +import org.apache.pdfbox.pdmodel.documentinterchange.markedcontent.PDPropertyList; import org.apache.pdfbox.pdmodel.graphics.PDXObject; - -import java.io.IOException; +import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpace; +import org.apache.pdfbox.pdmodel.graphics.pattern.PDAbstractPattern; +import org.apache.pdfbox.pdmodel.graphics.shading.PDShading; +import org.apache.pdfbox.pdmodel.graphics.state.PDExtendedGraphicsState; /** * @author: Sawyer.Yong @@ -14,7 +17,21 @@ import java.io.IOException; public class NotResourceCache extends DefaultResourceCache { @Override - public void put(COSObject indirect, PDXObject xobject) throws IOException { - // do nothing + public void put(COSObject indirect, PDColorSpace colorSpace) { + } + @Override + public void put(COSObject indirect, PDExtendedGraphicsState extGState) { + } + @Override + public void put(COSObject indirect, PDShading shading) { + } + @Override + public void put(COSObject indirect, PDAbstractPattern pattern) { + } + @Override + public void put(COSObject indirect, PDPropertyList propertyList) { + } + @Override + public void put(COSObject indirect, PDXObject xobject) { } }