diff --git a/jodconverter-web/pom.xml b/jodconverter-web/pom.xml index f51be380..f63cd4b1 100644 --- a/jodconverter-web/pom.xml +++ b/jodconverter-web/pom.xml @@ -161,6 +161,16 @@ rocksdbjni 5.17.2 + + org.apache.pdfbox + pdfbox + 2.0.15 + + + org.apache.pdfbox + pdfbox-tools + 2.0.15 + diff --git a/jodconverter-web/src/main/bin/startup.bat b/jodconverter-web/src/main/bin/startup.bat index 9447d3c2..1dff063f 100644 --- a/jodconverter-web/src/main/bin/startup.bat +++ b/jodconverter-web/src/main/bin/startup.bat @@ -4,4 +4,4 @@ cd "%KKFILEVIEW_BIN_FOLDER%" echo Using KKFILEVIEW_BIN_FOLDER %KKFILEVIEW_BIN_FOLDER% echo Starting kkFileView... echo Please check log file for more information -java -Dspring.config.location=..\conf\application.properties -jar kkFileView-0.1.jar -> ..\log\kkFileView.log \ No newline at end of file +java -Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider -Dspring.config.location=..\conf\application.properties -jar kkFileView-0.1.jar -> ..\log\kkFileView.log \ No newline at end of file diff --git a/jodconverter-web/src/main/bin/startup.sh b/jodconverter-web/src/main/bin/startup.sh index d08f8764..547f3627 100644 --- a/jodconverter-web/src/main/bin/startup.sh +++ b/jodconverter-web/src/main/bin/startup.sh @@ -27,4 +27,4 @@ else fi echo "Starting kkFileView..." echo "Please check log file for more information" -nohup java -Dspring.config.location=../conf/application.properties -jar kkFileView-0.1.jar > ../log/kkFileView.log 2>&1 & +nohup java -Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider -Dspring.config.location=../conf/application.properties -jar kkFileView-0.1.jar > ../log/kkFileView.log 2>&1 & diff --git a/jodconverter-web/src/main/conf/application.properties b/jodconverter-web/src/main/conf/application.properties index 53d18fd5..8dacf60d 100644 --- a/jodconverter-web/src/main/conf/application.properties +++ b/jodconverter-web/src/main/conf/application.properties @@ -34,3 +34,5 @@ spring.http.multipart.max-file-size=100MB #media = mp3,wav,mp4,flv #文件转换编码,默认根据操作系统获取 #converted.file.charset = GBK +#office类型文档(word ppt)样式,默认为图片(image),可配置为pdf(预览时也有按钮切换) +#office.preview.type = pdf diff --git a/jodconverter-web/src/main/java/cn/keking/config/ConfigConstants.java b/jodconverter-web/src/main/java/cn/keking/config/ConfigConstants.java index ded6cd01..44d3ec3d 100644 --- a/jodconverter-web/src/main/java/cn/keking/config/ConfigConstants.java +++ b/jodconverter-web/src/main/java/cn/keking/config/ConfigConstants.java @@ -17,6 +17,7 @@ public class ConfigConstants { private static String[] simText = {}; private static String[] media = {}; private static String convertedFileCharset; + private static String officePreviewType; private static String fileDir = OfficeUtils.getHomePath() + File.separator + "file" + File.separator; public static String[] getSimText() { @@ -43,6 +44,14 @@ public class ConfigConstants { ConfigConstants.convertedFileCharset = convertedFileCharset; } + public static String getOfficePreviewType() { + return officePreviewType; + } + + public static void setOfficePreviewType(String officePreviewType) { + ConfigConstants.officePreviewType = officePreviewType; + } + public static String getFileDir() { return fileDir; } diff --git a/jodconverter-web/src/main/java/cn/keking/config/ConfigRefreshComponent.java b/jodconverter-web/src/main/java/cn/keking/config/ConfigRefreshComponent.java index 329d8332..4654aaa2 100644 --- a/jodconverter-web/src/main/java/cn/keking/config/ConfigRefreshComponent.java +++ b/jodconverter-web/src/main/java/cn/keking/config/ConfigRefreshComponent.java @@ -1,5 +1,6 @@ package cn.keking.config; +import cn.keking.service.impl.OfficeFilePreviewImpl; import org.artofsolving.jodconverter.office.OfficeUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,8 +41,9 @@ public class ConfigRefreshComponent { String text; String media; String convertedFileCharset = sysProperties.getProperty("sun.jnu.encoding"); - String[] textArray ; + String[] textArray; String[] mediaArray; + String officePreviewType; String configFilePath = OfficeUtils.getCustomizedConfigPath(); while (true) { BufferedReader bufferedReader = new BufferedReader(new FileReader(configFilePath)); @@ -49,11 +51,13 @@ public class ConfigRefreshComponent { text = properties.getProperty("simText", DEFAULT_TXT_TYPE); media = properties.getProperty("media", DEFAULT_MEDIA_TYPE); convertedFileCharset = properties.getProperty("converted.file.charset", convertedFileCharset); + officePreviewType = properties.getProperty("office.preview.type", OfficeFilePreviewImpl.OFFICE_PREVIEW_TYPE_IMAGE); textArray = text.split(","); mediaArray = media.split(","); ConfigConstants.setSimText(textArray); ConfigConstants.setMedia(mediaArray); ConfigConstants.setConvertedFileCharset(convertedFileCharset); + ConfigConstants.setOfficePreviewType(officePreviewType); Thread.sleep(1000L); } } catch (IOException | InterruptedException e) { diff --git a/jodconverter-web/src/main/java/cn/keking/service/cache/CacheService.java b/jodconverter-web/src/main/java/cn/keking/service/cache/CacheService.java index fed1514b..386fac70 100644 --- a/jodconverter-web/src/main/java/cn/keking/service/cache/CacheService.java +++ b/jodconverter-web/src/main/java/cn/keking/service/cache/CacheService.java @@ -11,19 +11,26 @@ import java.util.Map; public interface CacheService { final String REDIS_FILE_PREVIEW_PDF_KEY = "converted-preview-pdf-file"; final String REDIS_FILE_PREVIEW_IMGS_KEY = "converted-preview-imgs-file";//压缩包内图片文件集合 + final String REDIS_FILE_PREVIEW_PDF_IMGS_KEY = "converted-preview-pdfimgs-file"; + final Integer DEFAULT_PDF_CAPACITY = 500000; final Integer DEFAULT_IMG_CAPACITY = 500000; + final Integer DEFAULT_PDFIMG_CAPACITY = 500000; void initPDFCachePool(Integer capacity); void initIMGCachePool(Integer capacity); + public void initPdfImagesCachePool(Integer capacity); void putPDFCache(String key, String value); void putImgCache(String key, List value); Map getPDFCache(); String getPDFCache(String key); Map> getImgCache(); List getImgCache(String key); + Integer getPdfImageCache(String key); + void putPdfImageCache(String pdfFilePath, int num); void addQueueTask(String url); String takeQueueTask() throws InterruptedException; + } diff --git a/jodconverter-web/src/main/java/cn/keking/service/cache/impl/CacheServiceJDKImpl.java b/jodconverter-web/src/main/java/cn/keking/service/cache/impl/CacheServiceJDKImpl.java index 8d48a257..7816b492 100644 --- a/jodconverter-web/src/main/java/cn/keking/service/cache/impl/CacheServiceJDKImpl.java +++ b/jodconverter-web/src/main/java/cn/keking/service/cache/impl/CacheServiceJDKImpl.java @@ -25,6 +25,8 @@ public class CacheServiceJDKImpl implements CacheService { private Map> imgCache; + private Map pdfImagesCache; + private static final int QUEUE_SIZE = 500000; private BlockingQueue blockingQueue = new ArrayBlockingQueue(QUEUE_SIZE); @@ -43,6 +45,13 @@ public class CacheServiceJDKImpl implements CacheService { .build(); } + @Override + public void initPdfImagesCachePool(Integer capacity) { + pdfImagesCache = new ConcurrentLinkedHashMap.Builder() + .maximumWeightedCapacity(capacity).weigher(Weighers.singleton()) + .build(); + } + @Override public void putPDFCache(String key, String value) { if (pdfCache == null) { @@ -91,6 +100,22 @@ public class CacheServiceJDKImpl implements CacheService { return imgCache.get(key); } + @Override + public Integer getPdfImageCache(String key) { + if (pdfImagesCache == null) { + initPdfImagesCachePool(CacheService.DEFAULT_PDFIMG_CAPACITY); + } + return pdfImagesCache.get(key); + } + + @Override + public void putPdfImageCache(String pdfFilePath, int num) { + if (pdfImagesCache == null) { + initPdfImagesCachePool(CacheService.DEFAULT_PDFIMG_CAPACITY); + } + pdfImagesCache.put(pdfFilePath, num); + } + @Override public void addQueueTask(String url) { blockingQueue.add(url); diff --git a/jodconverter-web/src/main/java/cn/keking/service/cache/impl/CacheServiceRedisImpl.java b/jodconverter-web/src/main/java/cn/keking/service/cache/impl/CacheServiceRedisImpl.java index 43335925..a4324426 100644 --- a/jodconverter-web/src/main/java/cn/keking/service/cache/impl/CacheServiceRedisImpl.java +++ b/jodconverter-web/src/main/java/cn/keking/service/cache/impl/CacheServiceRedisImpl.java @@ -43,6 +43,11 @@ public class CacheServiceRedisImpl implements CacheService { } + @Override + public void initPdfImagesCachePool(Integer capacity) { + + } + @Override public void putPDFCache(String key, String value) { RMapCache convertedList = redissonClient.getMapCache(REDIS_FILE_PREVIEW_PDF_KEY); @@ -77,6 +82,18 @@ public class CacheServiceRedisImpl implements CacheService { return convertedList.get(key); } + @Override + public Integer getPdfImageCache(String key) { + RMapCache convertedList = redissonClient.getMapCache(REDIS_FILE_PREVIEW_PDF_IMGS_KEY); + return convertedList.get(key); + } + + @Override + public void putPdfImageCache(String pdfFilePath, int num) { + RMapCache convertedList = redissonClient.getMapCache(REDIS_FILE_PREVIEW_PDF_IMGS_KEY); + convertedList.fastPut(pdfFilePath, num); + } + @Override public void addQueueTask(String url) { RBlockingQueue queue = redissonClient.getBlockingQueue(FileConverQueueTask.queueTaskName); diff --git a/jodconverter-web/src/main/java/cn/keking/service/cache/impl/CacheServiceRocksDBImpl.java b/jodconverter-web/src/main/java/cn/keking/service/cache/impl/CacheServiceRocksDBImpl.java index 5e84cfb4..6bc0feec 100644 --- a/jodconverter-web/src/main/java/cn/keking/service/cache/impl/CacheServiceRocksDBImpl.java +++ b/jodconverter-web/src/main/java/cn/keking/service/cache/impl/CacheServiceRocksDBImpl.java @@ -51,6 +51,10 @@ public class CacheServiceRocksDBImpl implements CacheService { Map> initIMGCache = new HashMap<>(); db.put(REDIS_FILE_PREVIEW_IMGS_KEY.getBytes(), toByteArray(initIMGCache)); } + if (db.get(REDIS_FILE_PREVIEW_PDF_IMGS_KEY.getBytes()) == null) { + Map initPDFIMGCache = new HashMap<>(); + db.put(REDIS_FILE_PREVIEW_PDF_IMGS_KEY.getBytes(), toByteArray(initPDFIMGCache)); + } } catch (RocksDBException | IOException e) { LOGGER.error("Uable to init RocksDB" + e); } @@ -67,6 +71,11 @@ public class CacheServiceRocksDBImpl implements CacheService { } + @Override + public void initPdfImagesCachePool(Integer capacity) { + + } + @Override public void putPDFCache(String key, String value) { try { @@ -136,6 +145,30 @@ public class CacheServiceRocksDBImpl implements CacheService { return result; } + @Override + public Integer getPdfImageCache(String key) { + Integer result = 0; + Map map; + try{ + map = (Map) toObject(db.get(REDIS_FILE_PREVIEW_PDF_IMGS_KEY.getBytes())); + result = map.get(key); + } catch (RocksDBException | IOException | ClassNotFoundException e) { + LOGGER.error("Get from RocksDB Exception" + e); + } + return result; + } + + @Override + public void putPdfImageCache(String pdfFilePath, int num) { + try { + Map pdfImageCacheItem = new HashMap<>(); + pdfImageCacheItem.put(pdfFilePath, num); + db.put(REDIS_FILE_PREVIEW_PDF_IMGS_KEY.getBytes(), toByteArray(pdfImageCacheItem)); + } catch (RocksDBException | IOException e) { + LOGGER.error("Put into RocksDB Exception" + e); + } + } + @Override public void addQueueTask(String url) { blockingQueue.add(url); diff --git a/jodconverter-web/src/main/java/cn/keking/service/impl/OfficeFilePreviewImpl.java b/jodconverter-web/src/main/java/cn/keking/service/impl/OfficeFilePreviewImpl.java index 11709b85..9825b287 100644 --- a/jodconverter-web/src/main/java/cn/keking/service/impl/OfficeFilePreviewImpl.java +++ b/jodconverter-web/src/main/java/cn/keking/service/impl/OfficeFilePreviewImpl.java @@ -7,6 +7,7 @@ import cn.keking.service.FilePreview; import cn.keking.utils.DownloadUtils; import cn.keking.utils.FileUtils; import cn.keking.utils.OfficeToPdf; +import cn.keking.utils.PdfUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; @@ -15,6 +16,7 @@ import org.springframework.ui.Model; import org.springframework.util.StringUtils; import java.io.File; +import java.util.List; /** * Created by kl on 2018/1/17. @@ -26,7 +28,8 @@ public class OfficeFilePreviewImpl implements FilePreview { @Autowired FileUtils fileUtils; - String fileDir = ConfigConstants.getFileDir(); + @Autowired + PdfUtils pdfUtils; @Autowired DownloadUtils downloadUtils; @@ -34,14 +37,22 @@ public class OfficeFilePreviewImpl implements FilePreview { @Autowired private OfficeToPdf officeToPdf; + String fileDir = ConfigConstants.getFileDir(); + + public static final String OFFICE_PREVIEW_TYPE_PDF = "pdf"; + public static final String OFFICE_PREVIEW_TYPE_IMAGE = "image"; + @Override public String filePreviewHandle(String url, Model model) { + // 预览Type,参数传了就取参数的,没传取系统默认 + String officePreviewType = model.asMap().get("officePreviewType") == null ? ConfigConstants.getOfficePreviewType() : model.asMap().get("officePreviewType").toString(); FileAttribute fileAttribute=fileUtils.getFileAttribute(url); String suffix=fileAttribute.getSuffix(); String fileName=fileAttribute.getName(); String decodedUrl=fileAttribute.getDecodedUrl(); boolean isHtml = suffix.equalsIgnoreCase("xls") || suffix.equalsIgnoreCase("xlsx"); String pdfName = fileName.substring(0, fileName.lastIndexOf(".") + 1) + (isHtml ? "html" : "pdf"); + String outFilePath = fileDir + pdfName; // 判断之前是否已转换过,如果转换过,直接返回,否则执行转换 if (!fileUtils.listConvertedFiles().containsKey(pdfName)) { String filePath = fileDir + fileName; @@ -53,7 +64,6 @@ public class OfficeFilePreviewImpl implements FilePreview { } filePath = response.getContent(); } - String outFilePath = fileDir + pdfName; if (StringUtils.hasText(outFilePath)) { officeToPdf.openOfficeToPDF(filePath, outFilePath); File f = new File(filePath); @@ -68,6 +78,17 @@ public class OfficeFilePreviewImpl implements FilePreview { fileUtils.addConvertedFile(pdfName, fileUtils.getRelativePath(outFilePath)); } } + if (OFFICE_PREVIEW_TYPE_IMAGE.equals(officePreviewType)) { + List imageUrls = pdfUtils.pdf2jpg(outFilePath, pdfName, url); + if (imageUrls == null || imageUrls.size() < 1) { + model.addAttribute("msg", "office转图片异常,请联系管理员"); + model.addAttribute("fileType",fileAttribute.getSuffix()); + return "fileNotSupported"; + } + model.addAttribute("imgurls", imageUrls); + model.addAttribute("currentUrl", imageUrls.get(0)); + return "officePicture"; + } model.addAttribute("pdfUrl", pdfName); return isHtml ? "html" : "pdf"; } diff --git a/jodconverter-web/src/main/java/cn/keking/utils/FileUtils.java b/jodconverter-web/src/main/java/cn/keking/utils/FileUtils.java index 4e241341..470051a6 100644 --- a/jodconverter-web/src/main/java/cn/keking/utils/FileUtils.java +++ b/jodconverter-web/src/main/java/cn/keking/utils/FileUtils.java @@ -47,6 +47,15 @@ public class FileUtils { return cacheService.getPDFCache(key); } + /** + * 已将pdf转换成图片的图片本地路径 + * @param key pdf本地路径 + * @return + */ + public Integer getConvertedPdfImage(String key) { + return cacheService.getPdfImageCache(key); + } + /** * 查看文件类型(防止参数中存在.点号或者其他特殊字符,所以先抽取文件名,然后再获取文件类型) * @@ -161,6 +170,15 @@ public class FileUtils { cacheService.putPDFCache(fileName, value); } + /** + * + * @param pdfFilePath + * @param num + */ + public void addConvertedPdfImage(String pdfFilePath, int num){ + cacheService.putPdfImageCache(pdfFilePath, num); + } + /** * 获取redis中压缩包内图片文件 * @param fileKey diff --git a/jodconverter-web/src/main/java/cn/keking/utils/PdfUtils.java b/jodconverter-web/src/main/java/cn/keking/utils/PdfUtils.java new file mode 100644 index 00000000..0052109c --- /dev/null +++ b/jodconverter-web/src/main/java/cn/keking/utils/PdfUtils.java @@ -0,0 +1,66 @@ +package cn.keking.utils; + +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.rendering.ImageType; +import org.apache.pdfbox.rendering.PDFRenderer; +import org.apache.pdfbox.tools.imageio.ImageIOUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +@Component +public class PdfUtils { + + private final Logger LOGGER = LoggerFactory.getLogger(PdfUtils.class); + + @Autowired + FileUtils fileUtils; + + public List pdf2jpg(String pdfFilePath, String pdfName, String url) { + List imageUrls = new ArrayList<>(); + Integer imageCount = fileUtils.getConvertedPdfImage(pdfFilePath); + String imageFileSuffix = ".jpg"; + // https://8个字符 http://7个字符 从这后面开始出现的第一个/就是当前file.Dir下的根目录 + int index1 = url.indexOf("/", 8); + String pdfFolder = pdfName.substring(0, pdfName.length() - 4); + String urlPrefix = url.substring(0, index1 + 1) + pdfFolder; + if (imageCount != null && imageCount.intValue() > 0) { + for (int i = 0; i < imageCount ; i++) + imageUrls.add(urlPrefix + "/" + i + imageFileSuffix); + return imageUrls; + } + try { + File pdfFile = new File(pdfFilePath); + PDDocument doc = PDDocument.load(pdfFile); + 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(); + } + String imageFilePath; + for (int pageIndex = 0; pageIndex < pageCount; pageIndex++) { + imageFilePath = folder + File.separator + pageIndex + imageFileSuffix; + BufferedImage image = pdfRenderer.renderImageWithDPI(pageIndex, 105, ImageType.RGB); + ImageIOUtil.writeImage(image, imageFilePath, 105); + imageUrls.add(urlPrefix + "/" + pageIndex + imageFileSuffix); + } + doc.close(); + fileUtils.addConvertedPdfImage(pdfFilePath, pageCount); + } catch (IOException e) { + LOGGER.error("Convert pdf to jpg exception", e); + } + return imageUrls; + } +} diff --git a/jodconverter-web/src/main/java/cn/keking/web/controller/OnlinePreviewController.java b/jodconverter-web/src/main/java/cn/keking/web/controller/OnlinePreviewController.java index 3adc8dfd..4eb65c97 100644 --- a/jodconverter-web/src/main/java/cn/keking/web/controller/OnlinePreviewController.java +++ b/jodconverter-web/src/main/java/cn/keking/web/controller/OnlinePreviewController.java @@ -45,6 +45,7 @@ public class OnlinePreviewController { @RequestMapping(value = "onlinePreview", method = RequestMethod.GET) public String onlinePreview(String url, Model model, HttpServletRequest req) { req.setAttribute("fileKey", req.getParameter("fileKey")); + model.addAttribute("officePreviewType", req.getParameter("officePreviewType")); FilePreview filePreview = previewFactory.get(url); return filePreview.filePreviewHandle(url, model); } diff --git a/jodconverter-web/src/main/resources/static/images/left.png b/jodconverter-web/src/main/resources/static/images/left.png new file mode 100644 index 00000000..7db8f800 Binary files /dev/null and b/jodconverter-web/src/main/resources/static/images/left.png differ diff --git a/jodconverter-web/src/main/resources/static/images/loading.gif b/jodconverter-web/src/main/resources/static/images/loading.gif new file mode 100644 index 00000000..78fd259b Binary files /dev/null and b/jodconverter-web/src/main/resources/static/images/loading.gif differ diff --git a/jodconverter-web/src/main/resources/static/images/right.png b/jodconverter-web/src/main/resources/static/images/right.png new file mode 100644 index 00000000..ab726c03 Binary files /dev/null and b/jodconverter-web/src/main/resources/static/images/right.png differ diff --git a/jodconverter-web/src/main/resources/static/js/lazyload.js b/jodconverter-web/src/main/resources/static/js/lazyload.js new file mode 100644 index 00000000..8376ed45 --- /dev/null +++ b/jodconverter-web/src/main/resources/static/js/lazyload.js @@ -0,0 +1,41 @@ +function isInSight(el) { + const bound = el.getBoundingClientRect(); + const clientHeight = window.innerHeight; + //只考虑向下滚动加载 + //const clientWidth=window.innerWeight; + return bound.top <= clientHeight + 100; +} + +let index = 0; +function checkImgs() { + const imgs = document.querySelectorAll('.my-photo'); + for (let i = index; i < imgs.length; i++) { + if (isInSight(imgs[i])) { + loadImg(imgs[i]); + index = i; + } + } +} + +function loadImg(el) { + const source = el.dataset.src; + el.src = source; +} + +function throttle(fn, mustRun = 500) { + const timer = null; + let previous = null; + return function() { + const now = new Date(); + const context = this; + const args = arguments; + if (!previous) { + previous = now; + } + const remaining = now - previous; + if (mustRun && remaining >= mustRun) { + fn.apply(context, args); + previous = now; + } + } +} diff --git a/jodconverter-web/src/main/resources/web/officePicture.ftl b/jodconverter-web/src/main/resources/web/officePicture.ftl new file mode 100644 index 00000000..0f8d238a --- /dev/null +++ b/jodconverter-web/src/main/resources/web/officePicture.ftl @@ -0,0 +1,40 @@ + + + + + office图片预览 + + + + + + <#list imgurls as img> + + + + #list> + + + + + \ No newline at end of file diff --git a/jodconverter-web/src/main/resources/web/pdf.ftl b/jodconverter-web/src/main/resources/web/pdf.ftl index 665a01c1..27ef95e8 100644 --- a/jodconverter-web/src/main/resources/web/pdf.ftl +++ b/jodconverter-web/src/main/resources/web/pdf.ftl @@ -17,6 +17,9 @@ <#assign finalUrl="${baseUrl}${pdfUrl}"> #if> + + +