浏览代码

word、ppt文档新增图片预览模式

陈精华 6 年之前
父节点
当前提交
b4d3419797
共有 20 个文件被更改,包括 312 次插入5 次删除
  1. 10 0
      jodconverter-web/pom.xml
  2. 1 1
      jodconverter-web/src/main/bin/startup.bat
  3. 1 1
      jodconverter-web/src/main/bin/startup.sh
  4. 2 0
      jodconverter-web/src/main/conf/application.properties
  5. 9 0
      jodconverter-web/src/main/java/cn/keking/config/ConfigConstants.java
  6. 5 1
      jodconverter-web/src/main/java/cn/keking/config/ConfigRefreshComponent.java
  7. 7 0
      jodconverter-web/src/main/java/cn/keking/service/cache/CacheService.java
  8. 25 0
      jodconverter-web/src/main/java/cn/keking/service/cache/impl/CacheServiceJDKImpl.java
  9. 17 0
      jodconverter-web/src/main/java/cn/keking/service/cache/impl/CacheServiceRedisImpl.java
  10. 33 0
      jodconverter-web/src/main/java/cn/keking/service/cache/impl/CacheServiceRocksDBImpl.java
  11. 23 2
      jodconverter-web/src/main/java/cn/keking/service/impl/OfficeFilePreviewImpl.java
  12. 18 0
      jodconverter-web/src/main/java/cn/keking/utils/FileUtils.java
  13. 66 0
      jodconverter-web/src/main/java/cn/keking/utils/PdfUtils.java
  14. 1 0
      jodconverter-web/src/main/java/cn/keking/web/controller/OnlinePreviewController.java
  15. 二进制
      jodconverter-web/src/main/resources/static/images/left.png
  16. 二进制
      jodconverter-web/src/main/resources/static/images/loading.gif
  17. 二进制
      jodconverter-web/src/main/resources/static/images/right.png
  18. 41 0
      jodconverter-web/src/main/resources/static/js/lazyload.js
  19. 40 0
      jodconverter-web/src/main/resources/web/officePicture.ftl
  20. 13 0
      jodconverter-web/src/main/resources/web/pdf.ftl

+ 10 - 0
jodconverter-web/pom.xml

@@ -161,6 +161,16 @@
             <artifactId>rocksdbjni</artifactId>
             <version>5.17.2</version>
         </dependency>
+        <dependency>
+            <groupId>org.apache.pdfbox</groupId>
+            <artifactId>pdfbox</artifactId>
+            <version>2.0.15</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.pdfbox</groupId>
+            <artifactId>pdfbox-tools</artifactId>
+            <version>2.0.15</version>
+        </dependency>
     </dependencies>
     <build>
         <resources>

+ 1 - 1
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
+java -Dsun.java2d.cmm=sun.java2d.cmm.kcms.KcmsServiceProvider -Dspring.config.location=..\conf\application.properties -jar kkFileView-0.1.jar -> ..\log\kkFileView.log

+ 1 - 1
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 &

+ 2 - 0
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

+ 9 - 0
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;
     }

+ 5 - 1
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) {

+ 7 - 0
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<String> value);
     Map<String, String> getPDFCache();
     String getPDFCache(String key);
     Map<String, List<String>> getImgCache();
     List<String> getImgCache(String key);
+    Integer getPdfImageCache(String key);
+    void putPdfImageCache(String pdfFilePath, int num);
 
     void addQueueTask(String url);
     String takeQueueTask() throws InterruptedException;
+
 }

+ 25 - 0
jodconverter-web/src/main/java/cn/keking/service/cache/impl/CacheServiceJDKImpl.java

@@ -25,6 +25,8 @@ public class CacheServiceJDKImpl implements CacheService {
 
     private Map<String, List<String>> imgCache;
 
+    private Map<String, Integer> 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<String, Integer>()
+                .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);

+ 17 - 0
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<String, String> 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<String, Integer> convertedList = redissonClient.getMapCache(REDIS_FILE_PREVIEW_PDF_IMGS_KEY);
+        return convertedList.get(key);
+    }
+
+    @Override
+    public void putPdfImageCache(String pdfFilePath, int num) {
+        RMapCache<String, Integer> convertedList = redissonClient.getMapCache(REDIS_FILE_PREVIEW_PDF_IMGS_KEY);
+        convertedList.fastPut(pdfFilePath, num);
+    }
+
     @Override
     public void addQueueTask(String url) {
         RBlockingQueue<String> queue = redissonClient.getBlockingQueue(FileConverQueueTask.queueTaskName);

+ 33 - 0
jodconverter-web/src/main/java/cn/keking/service/cache/impl/CacheServiceRocksDBImpl.java

@@ -51,6 +51,10 @@ public class CacheServiceRocksDBImpl implements CacheService {
                 Map<String, List<String>> 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<String, Integer> 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<String, Integer> map;
+        try{
+            map = (Map<String, Integer>) 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<String, Integer> 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);

+ 23 - 2
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<String> 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";
     }

+ 18 - 0
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

+ 66 - 0
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<String> pdf2jpg(String pdfFilePath, String pdfName, String url) {
+        List<String> 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;
+    }
+}

+ 1 - 0
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);
     }

二进制
jodconverter-web/src/main/resources/static/images/left.png


二进制
jodconverter-web/src/main/resources/static/images/loading.gif


二进制
jodconverter-web/src/main/resources/static/images/right.png


+ 41 - 0
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;
+    }
+  }
+}

+ 40 - 0
jodconverter-web/src/main/resources/web/officePicture.ftl

@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="utf-8" />
+    <title>office图片预览</title>
+    <script src="js/lazyload.js"></script>
+    <style>
+        .container{
+            width:100%;
+        }
+        .img-area{
+            text-align: center
+        }
+
+    </style>
+</head>
+<body>
+<div class="container">
+    <#list imgurls as img>
+        <div class="img-area">
+            <img class="my-photo" alt="loading" data-src="${img}" src="images/loading.gif">
+        </div>
+    </#list>
+</div>
+<img src="images/left.png" style="position: fixed; cursor: pointer; top: 40%; left: 50px; z-index: 999;" alt="PDF预览" onclick="goForPdf()"/>
+<script>
+    window.onload=checkImgs;
+    window.onscroll = throttle(checkImgs);
+    function goForPdf() {
+        var url = window.location.href;
+        if (url.indexOf("officePreviewType=image") != -1) {
+            url = url.replace("officePreviewType=image", "officePreviewType=pdf");
+        } else {
+            url = url + "&officePreviewType=pdf";
+        }
+        window.location.href=url;
+    }
+</script>
+</body>
+</html>

+ 13 - 0
jodconverter-web/src/main/resources/web/pdf.ftl

@@ -17,6 +17,9 @@
         <#assign finalUrl="${baseUrl}${pdfUrl}">
     </#if>
     <iframe src="/pdfjs/web/viewer.html?file=${finalUrl}" width="100%" frameborder="0"></iframe>
+
+    <img src="images/right.png" style="position: fixed; cursor: pointer; top: 40%; right: 50px; z-index: 999;" alt="图片预览" onclick="goForImage()"/>
+
 </body>
 <script type="text/javascript">
     document.getElementsByTagName('iframe')[0].height = document.documentElement.clientHeight-10;
@@ -27,5 +30,15 @@
         var fm = document.getElementsByTagName("iframe")[0];
         fm.height = window.document.documentElement.clientHeight-10;
     }
+
+    function goForImage() {
+        var url = window.location.href;
+        if (url.indexOf("officePreviewType=pdf") != -1) {
+            url = url.replace("officePreviewType=pdf", "officePreviewType=image");
+        } else {
+            url = url + "&officePreviewType=image";
+        }
+        window.location.href=url;
+    }
 </script>
 </html>