瀏覽代碼

daqian modify for file upload

daqian 4 年之前
父節點
當前提交
4749bc3c10

+ 156 - 0
.gitignore

@@ -0,0 +1,156 @@
+# Created by .ignore support plugin (hsz.mobi)
+### Java template
+# Compiled class file
+*.class
+
+# Log file
+*.log
+
+# BlueJ files
+*.ctxt
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.jar
+*.war
+*.nar
+*.ear
+*.zip
+*.tar.gz
+*.rar
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+
+### JetBrains template
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+
+# User-specific stuff
+.idea/**/workspace.xml
+.idea/**/tasks.xml
+.idea/**/usage.statistics.xml
+.idea/**/dictionaries
+.idea/**/shelf
+
+# Generated files
+.idea/**/contentModel.xml
+
+# Sensitive or high-churn files
+.idea/**/dataSources/
+.idea/**/dataSources.ids
+.idea/**/dataSources.local.xml
+.idea/**/sqlDataSources.xml
+.idea/**/dynamic.xml
+.idea/**/uiDesigner.xml
+.idea/**/dbnavigator.xml
+
+# Gradle
+.idea/**/gradle.xml
+.idea/**/libraries
+
+# Gradle and Maven with auto-import
+# When using Gradle or Maven with auto-import, you should exclude module files,
+# since they will be recreated, and may cause churn.  Uncomment if using
+# auto-import.
+# .idea/artifacts
+# .idea/compiler.xml
+# .idea/modules.xml
+# .idea/*.iml
+# .idea/modules
+# *.iml
+# *.ipr
+
+# CMake
+cmake-build-*/
+
+# Mongo Explorer plugin
+.idea/**/mongoSettings.xml
+
+# File-based project format
+*.iws
+
+# IntelliJ
+out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Cursive Clojure plugin
+.idea/replstate.xml
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties
+
+# Editor-based Rest Client
+.idea/httpRequests
+
+# Android studio 3.1+ serialized cache file
+.idea/caches/build_file_checksums.ser
+
+### Eclipse template
+.metadata
+bin/
+tmp/
+*.tmp
+*.bak
+*.swp
+*~.nib
+local.properties
+.settings/
+.loadpath
+.recommenders
+
+# External tool builders
+.externalToolBuilders/
+
+# Locally stored "Eclipse launch configurations"
+*.launch
+
+# PyDev specific (Python IDE for Eclipse)
+*.pydevproject
+
+# CDT-specific (C/C++ Development Tooling)
+.cproject
+
+# CDT- autotools
+.autotools
+
+# Java annotation processor (APT)
+.factorypath
+
+# PDT-specific (PHP Development Tools)
+.buildpath
+
+# sbteclipse plugin
+.target
+
+# Tern plugin
+.tern-project
+
+# TeXlipse plugin
+.texlipse
+
+# STS (Spring Tool Suite)
+.springBeans
+
+# Code Recommenders
+.recommenders/
+
+# Annotation Processing
+.apt_generated/
+.apt_generated_test/
+
+# Scala IDE specific (Scala & Java development for Eclipse)
+.cache-main
+.scala_dependencies
+.worksheet
+

+ 29 - 1
pom.xml

@@ -171,6 +171,12 @@
             <artifactId>commons-lang3</artifactId>
             <version>3.4</version>
         </dependency>
+
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>1.3.2</version>
+        </dependency>
         <!-- 阿里云短信jar包 start -->
         <dependency>
             <groupId>com.aliyun</groupId>
@@ -201,6 +207,12 @@
             <version>4.5.3</version>
         </dependency>
 
+        <dependency>
+            <groupId>commons-httpclient</groupId>
+            <artifactId>commons-httpclient</artifactId>
+            <version>3.1</version>
+        </dependency>
+
         <dependency>
             <groupId>org.projectlombok</groupId>
             <artifactId>lombok</artifactId>
@@ -208,7 +220,23 @@
             <scope>provided</scope>
         </dependency>
 
-
+        <!--office document preview & export-->
+        <dependency>
+            <groupId>org.jodconverter</groupId>
+            <artifactId>jodconverter-local</artifactId>
+            <version>4.3.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.icepdf.os</groupId>
+            <artifactId>icepdf-core</artifactId>
+            <version>6.2.2</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>javax.media</groupId>
+                    <artifactId>jai_core</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
     </dependencies>
 
 

+ 31 - 1
src/main/java/com/jfinal/club/common/JFinalClubConfig.java

@@ -36,12 +36,15 @@ import com.jfinal.plugin.ehcache.EhCachePlugin;
 import com.jfinal.render.JsonRender;
 import com.jfinal.server.undertow.UndertowServer;
 import com.jfinal.template.Engine;
+import lombok.extern.log4j.Log4j;
+import org.jodconverter.local.office.LocalOfficeManager;
 
 import java.sql.Connection;
 
 /**
  * JFinalClubConfig
  */
+@Log4j
 public class JFinalClubConfig extends JFinalConfig {
 
     // 使用 jfinal-undertow 时此处仅保留声明,不能有加载代码
@@ -49,6 +52,11 @@ public class JFinalClubConfig extends JFinalConfig {
 
     private WallFilter wallFilter;
 
+    /**
+     * 文档转换用
+     */
+    private final LocalOfficeManager officeManager = LocalOfficeManager.install();
+
     /**
      * 启动入口,运行此 main 方法可以启动项目,此 main 方法
      * 可以放置在任意的 Class 类定义中,不一定要放于此
@@ -67,9 +75,10 @@ public class JFinalClubConfig extends JFinalConfig {
         }
     }
 
+    @Override
     public void configConstant(Constants me) {
         loadConfig();
-
+        me.setMaxPostSize(104857600);
         me.setDevMode(p.getBoolean("devMode", false));
         me.setJsonFactory(MixedJsonFactory.me());
 
@@ -87,6 +96,7 @@ public class JFinalClubConfig extends JFinalConfig {
      * 3:避免本文件中内容过多,拆分后可读性增强
      * 4:便于分模块管理路由
      */
+    @Override
     public void configRoute(Routes me) {
         me.add(new FrontRoutes());
         me.add(new AdminRoutes());
@@ -95,6 +105,7 @@ public class JFinalClubConfig extends JFinalConfig {
     /**
      * 配置模板引擎,通常情况只需配置共享的模板函数
      */
+    @Override
     public void configEngine(Engine me) {
         me.setDevMode(p.getBoolean("engineDevMode", false));
 
@@ -138,6 +149,7 @@ public class JFinalClubConfig extends JFinalConfig {
         return new DruidPlugin(p.get("jdbcUrl"), p.get("user"), p.get("password").trim());
     }
 
+    @Override
     public void configPlugin(Plugins me) {
         DruidPlugin druidPlugin = getDruidPlugin();
         wallFilter = new WallFilter();            // 加强数据库安全
@@ -161,10 +173,12 @@ public class JFinalClubConfig extends JFinalConfig {
         me.add(new Cron4jPlugin(p));
     }
 
+    @Override
     public void configInterceptor(Interceptors me) {
         me.add(new LoginSessionInterceptor());
     }
 
+    @Override
     public void configHandler(Handlers me) {
         me.add(DruidKit.getDruidStatViewHandler()); // druid 统计页面功能
         me.add(new UrlSeoHandler());                // index、detail 两类 action 的 url seo
@@ -173,6 +187,7 @@ public class JFinalClubConfig extends JFinalConfig {
     /**
      * 本方法会在 jfinal 启动过程完成之后被回调,详见 jfinal 手册
      */
+    @Override
     public void onStart() {
         // 调用不带参的 renderJson() 时,排除对 loginAccount、remind 的 json 转换
         JsonRender.addExcludedAttrs(
@@ -183,6 +198,21 @@ public class JFinalClubConfig extends JFinalConfig {
         // 让 druid 允许在 sql 中使用 union
         // https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE-wallfilter
         wallFilter.getConfig().setSelectUnionCheck(false);
+        try {
+            officeManager.start();
+            log.info("office manager started!");
+        }catch (Exception ex){
+            log.error("start office manager",ex);
+        }
+    }
+
+    @Override
+    public void onStop(){
+        try {
+            officeManager.stop();
+        }catch (Exception ex){
+            log.error("stop office manager",ex);
+        }
     }
 }
 

+ 1 - 1
src/main/java/com/jfinal/club/common/banner/BannersService.java

@@ -17,7 +17,7 @@ public class BannersService {
      * @Param [type]
      **/
     public Page<Banners> getBannersByType(int type, int size) {
-        return bannersdao.paginate(1, size, "seletc *", "from banners where type = ? order by sort desc ,addtime desc", type);
+        return bannersdao.paginate(1, size, "select *", "from banners where type = ? order by sort desc ,addtime desc", type);
     }
 
 

+ 180 - 0
src/main/java/com/jfinal/club/common/doc/convert/PdfTransferUtil.java

@@ -0,0 +1,180 @@
+package com.jfinal.club.common.doc.convert;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.FilenameUtils;
+import org.icepdf.core.pobjects.Document;
+import org.icepdf.core.pobjects.Page;
+import org.icepdf.core.util.GraphicsRenderingHints;
+
+import javax.imageio.ImageIO;
+import javax.imageio.stream.ImageOutputStream;
+import java.awt.image.BufferedImage;
+import java.awt.image.RenderedImage;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.InputStream;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+/**
+ * @author lr
+ *  
+ */
+@Slf4j
+public class PdfTransferUtil {
+
+    //*********************************pdf to image **********************************************************
+
+    /**
+     * 将指定pdf字节数组转换为指定格式图片二进制数组
+     *
+     * @param pdfBytes  PDF字节数组
+     * @param imageType 转换图片格式  默认png
+     * @param zoom      缩略图显示倍数,1表示不缩放,0.3则缩小到30%
+     * @return List<byte [ ]>
+     * @throws Exception
+     */
+    public List<byte[]> pdf2Image(byte[] pdfBytes, String imageType, float zoom) throws Exception {
+        Document document = new Document();
+        document.setByteArray(pdfBytes, 0, pdfBytes.length, null);
+        return pageExtraction(document, imageType, 0f, zoom);
+    }
+
+    /**
+     * 将指定pdf输入流转换为指定格式图片二进制数组
+     *
+     * @param inputPDF  PDF二进制流
+     * @param imageType 转换图片格式 默认png
+     * @param zoom      缩略图显示倍数,1表示不缩放,0.3则缩小到30%
+     * @return List<byte [ ]>
+     * @throws Exception
+     */
+    public List<byte[]> pdf2Image(InputStream inputPDF, String imageType, float zoom) throws Exception {
+
+        Document document = new Document();
+        document.setInputStream(inputPDF, null);
+        return pageExtraction(document, imageType, 0f, zoom);
+    }
+
+    /**
+     * 将指定pdf文件转换为指定格式图片二进制数组
+     *
+     * @param pdfPath   原文件路径,例如d:/test.pdf
+     * @param imageType 转换图片格式 默认png
+     * @param zoom      缩略图显示倍数,1表示不缩放,0.3则缩小到30%
+     * @return List<byte [ ]>
+     * @throws Exception
+     */
+    public List<byte[]> pdf2Image(String pdfPath, String imageType, float zoom) throws Exception {
+        Document document = new Document();
+        document.setFile(pdfPath);
+        return pageExtraction(document, imageType, 0f, zoom);
+    }
+    //*********************************pdf to image **********************************************************
+
+    private List<byte[]> pageExtraction(Document document, String imageType, float rotation, float zoom) {
+        // setup two threads to handle image extraction.
+        ExecutorService executorService = Executors.newFixedThreadPool(5);
+        try {
+            // create a list of callables.
+            int pages = document.getNumberOfPages();
+            List<byte[]> result = new ArrayList<byte[]>(pages);
+            List<Callable<byte[]>> callables = new ArrayList<Callable<byte[]>>(pages);
+            for (int i = 0; i < pages; i++) {
+                callables.add(new CapturePage(document, i, imageType, rotation, zoom));
+            }
+            List<Future<byte[]>> listFuture = executorService.invokeAll(callables);
+            executorService.submit(new DocumentCloser(document)).get();
+            for (Future<byte[]> future : listFuture) {
+                result.add(future.get());
+            }
+            return result;
+        } catch (Exception ex) {
+            log.error(" pdf 转换图片错误  Error handling PDF document " + ex);
+        } finally {
+            executorService.shutdown();
+        }
+        return null;
+    }
+
+    public static String pdf2Pic(String pdfPath){
+        try{
+            File returnFile = Paths.get(FilenameUtils.getPath(pdfPath),FilenameUtils.getBaseName(pdfPath)+".png").toFile();
+            Document document = new Document();
+            document.setFile(pdfPath);
+            //缩放比例
+            float scale = 2.5f;
+            //旋转角度
+            float rotation = 0f;
+
+            //for (int i = 0; i < document.getNumberOfPages(); i++) {
+                BufferedImage image = (BufferedImage)document.getPageImage(0, GraphicsRenderingHints.SCREEN, org.icepdf.core.pobjects.Page.BOUNDARY_CROPBOX, rotation, scale);
+                RenderedImage rendImage = image;
+                try {
+                    ImageIO.write(rendImage, "png", returnFile);
+                } catch (Exception e) {
+                    log.error("pdf2Pic",e);
+                }
+                image.flush();
+            //}
+            document.dispose();
+            return returnFile.getPath();
+        }catch (Exception ex){
+            log.error("pdf2Pic",ex);
+        }
+        return null;
+    }
+
+
+    public class CapturePage implements Callable<byte[]> {
+        private Document document;
+        private int pageNumber;
+        private String imageType;
+        private float rotation;
+        private float zoom;
+
+        private CapturePage(Document document, int pageNumber, String imageType, float rotation, float zoom) {
+            this.document = document;
+            this.pageNumber = pageNumber;
+            this.imageType = imageType;
+            this.rotation = rotation;
+            this.zoom = zoom;
+        }
+
+        @Override
+        public byte[] call() throws Exception {
+            BufferedImage image = (BufferedImage) document.getPageImage(pageNumber, GraphicsRenderingHints.SCREEN, Page.BOUNDARY_CROPBOX, rotation, zoom);
+            ByteArrayOutputStream bs = new ByteArrayOutputStream();
+            ImageOutputStream imOut = ImageIO.createImageOutputStream(bs);
+            ImageIO.write(image, imageType, imOut);
+            image.flush();
+            return bs.toByteArray();
+        }
+    }
+
+    /**
+     * Disposes the document.
+     */
+    public class DocumentCloser implements Callable<Void> {
+        private Document document;
+
+        private DocumentCloser(Document document) {
+            this.document = document;
+        }
+
+        @Override
+        public Void call() {
+            if (document != null) {
+                document.dispose();
+                log.info("Document disposed");
+            }
+            return null;
+        }
+    }
+
+}

+ 132 - 0
src/main/java/com/jfinal/club/common/doc/convert/TransferUtil.java

@@ -0,0 +1,132 @@
+package com.jfinal.club.common.doc.convert;
+
+import com.jfinal.upload.UploadFile;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.jodconverter.local.JodConverter;
+
+import java.io.File;
+import java.nio.file.Paths;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.List;
+
+;
+
+/**
+ * word 转pdf
+ *
+ * @author lr
+ */
+@Slf4j
+public class TransferUtil {
+
+    private static String[] officeExtNames = new String[]{"doc","docx","ppt","pptx","xls","xlsx"};
+    private static String[] skipExtNames = new String[]{"txt","pdf"};
+
+    private static String datetimePattern = "yyyyMMddHHmmssSSS";
+    /**
+     * 根据拓展名判断是否是OFFICE文件
+     * @param extName 拓展名
+     * @return
+     */
+    public static boolean isOfficeFile(String extName){
+        return ArrayUtils.contains(officeExtNames,extName);
+    }
+
+    /**
+     * word,excel,ppt ->pdf
+     *
+     * @return 相同文件夹下的转换后的pdf文件 如/test/wd_20190517151515333.pdf
+     * @throws Exception
+     */
+    public static File transferWordToPdf(String originLocalFilePath) throws Exception {
+        //转换成本地实际磁盘路径
+        File inputFile = new File(originLocalFilePath);
+
+        if (!inputFile.exists() || !inputFile.isFile() || isOfficeFile(FilenameUtils.getExtension(originLocalFilePath))) {
+            throw new Exception("文件 -> pdf转换错误  当前文件不是office 文档或文件不存在: " + originLocalFilePath);
+        }
+
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(datetimePattern);
+        String timeNow = formatter.format(LocalDateTime.now());
+
+        File newPdfWebPath = Paths.get(FilenameUtils.getPath(originLocalFilePath),"pdf",timeNow+".pdf").toFile();
+        try {
+            JodConverter.convert(inputFile).to(newPdfWebPath).execute();
+        } catch (Exception e) {
+            log.error("word->pdf 转换错误------------> Exception: ", e);
+            throw e;
+        }
+        return newPdfWebPath;
+    }
+
+    /**
+     * pdf转图片 返回本地存储路径图片集合
+     *
+     * @param originLocalFilePath pdf 文件路径
+     * @return
+     * @throws Exception
+     */
+    public static List<String> transferPdfToImage(String originLocalFilePath) throws Exception {
+        File inputFile = new File(originLocalFilePath);
+        if (!inputFile.exists() ||
+                !inputFile.isFile() ||
+                originLocalFilePath.lastIndexOf(".pdf") < 0) {
+            throw new Exception("pdf-> img 源文件不是pdf文件 或者文件不存在!");
+        }
+        String localPdfPath = originLocalFilePath;
+
+        PdfTransferUtil transferUtil = new PdfTransferUtil();
+        List<byte[]> ins = transferUtil.pdf2Image(localPdfPath, "png", 1.5f);
+        List<String> localFilePath = new ArrayList<>(ins.size());
+        for (int i = 0; i < ins.size(); i++) {
+            byte[] data = ins.get(i);
+            File pathReal = Paths.get(FilenameUtils.getPath(originLocalFilePath)+ "..",FilenameUtils.getBaseName(originLocalFilePath) + "_" + i + ".png").toFile();
+            FileUtils.writeByteArrayToFile(pathReal, data);
+            localFilePath.add(pathReal.toString());
+        }
+        return localFilePath;
+    }
+
+    /**
+     * pdf转图片 返回web可访问图片集合
+     *
+     * @param webPath
+     * @return
+     * @throws Exception
+     */
+    public static List<String> transferPdfToWebImage(String webPath) throws Exception {
+        List<String> localPathImage = transferPdfToImage(webPath);
+        List<String> webPathImage = new ArrayList<>(localPathImage.size());
+        for (String s : localPathImage) {
+            webPathImage.add(s);
+        }
+        return webPathImage;
+    }
+
+    /**
+     * 上传文件保存到本地,返回可访问web路径
+     *
+     * @param suffix
+     * @param file
+     * @return
+     * @throws Exception
+     */
+    public static String saveLocation(String suffix, UploadFile file) throws Exception {
+        String fileName = System.currentTimeMillis() + "." + suffix;
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(datetimePattern);
+        String time = formatter.format(LocalDateTime.now());
+
+        File saveFile = Paths.get(file.getUploadPath(),  time + "."+FilenameUtils.getExtension(file.getFileName())).toFile();
+        file.getFile().renameTo(saveFile);
+
+        log.info("保存的文件路径--> fileLocalSavePath:{}", saveFile.getAbsolutePath());
+        return saveFile.getPath();
+    }
+
+}

+ 7 - 0
src/main/java/com/jfinal/club/index/IndexController.java

@@ -27,6 +27,13 @@ import com.jfinal.club.my.doc.TypeIntreceptor;
 import com.jfinal.club.my.doc.UsersDocService;
 import com.jfinal.club.my.ques.QuestionsService;
 import com.jfinal.plugin.activerecord.Page;
+import org.jodconverter.core.DocumentConverter;
+import org.jodconverter.core.office.OfficeException;
+import org.jodconverter.core.office.OfficeUtils;
+import org.jodconverter.local.JodConverter;
+import org.jodconverter.local.office.LocalOfficeManager;
+
+import java.io.File;
 
 /**
  * 首页控制器

+ 1 - 1
src/main/java/com/jfinal/club/my/doc/UsersDocService.java

@@ -36,7 +36,7 @@ public class UsersDocService {
 
     // 用户上传图像最多只允许 1M大小
     public int getAvatarMaxSize() {
-        return 1024 * 1024;
+        return 1024 * 1024 * 100;
     }
 
     /**