xiatian 4 weeks ago
parent
commit
fe69403ac6
27 changed files with 2980 additions and 3 deletions
  1. 53 0
      .gitignore
  2. 0 3
      README.md
  3. BIN
      columns/columns.xlsx
  4. 113 0
      pom.xml
  5. 2 0
      sql/work.sql
  6. 22 0
      src/main/java/cn/com/qmth/archive/ArchiveApplication.java
  7. 8 0
      src/main/java/cn/com/qmth/archive/bean/TableCols.java
  8. 20 0
      src/main/java/cn/com/qmth/archive/config/InitData.java
  9. 20 0
      src/main/java/cn/com/qmth/archive/config/SysProperty.java
  10. 11 0
      src/main/java/cn/com/qmth/archive/dao/MarkingDao.java
  11. 10 0
      src/main/java/cn/com/qmth/archive/service/MarkingService.java
  12. 122 0
      src/main/java/cn/com/qmth/archive/service/impl/MarkingServiceImpl.java
  13. 40 0
      src/main/java/cn/com/qmth/archive/utils/BatchSetDataUtil.java
  14. 200 0
      src/main/java/cn/com/qmth/archive/utils/Calculator.java
  15. 208 0
      src/main/java/cn/com/qmth/archive/utils/DateUtil.java
  16. 899 0
      src/main/java/cn/com/qmth/archive/utils/FileUtil.java
  17. 51 0
      src/main/java/cn/com/qmth/archive/utils/FormFilePart.java
  18. 108 0
      src/main/java/cn/com/qmth/archive/utils/HttpMethod.java
  19. 376 0
      src/main/java/cn/com/qmth/archive/utils/ImageUtil.java
  20. 96 0
      src/main/java/cn/com/qmth/archive/utils/JsonHelper.java
  21. 88 0
      src/main/java/cn/com/qmth/archive/utils/MD5Util.java
  22. 277 0
      src/main/java/cn/com/qmth/archive/utils/OKHttpUtil.java
  23. 64 0
      src/main/java/cn/com/qmth/archive/utils/ResouceUtil.java
  24. 63 0
      src/main/java/cn/com/qmth/archive/utils/SSLSocketClient.java
  25. 74 0
      src/main/java/cn/com/qmth/archive/utils/SpringContextHolder.java
  26. 39 0
      src/main/resources/application.properties
  27. 16 0
      src/main/resources/mapper/MarkingMapper.xml

+ 53 - 0
.gitignore

@@ -0,0 +1,53 @@
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+*.class
+*.log
+
+
+### Eclipse & STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+/logs/
+/log/
+/data/
+
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+
+### VS Code ###
+.vscode
+node_modules
+package-lock.json
+yarn.lock
+
+
+### Package Files ###
+*.zip
+*.war
+*.ear
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+target/
+
+.flattened-pom.xml
+.DS_Store
+

+ 0 - 3
README.md

@@ -1,3 +0,0 @@
-# examcloud-archive-tool
-
-云平台数据归档小工具

BIN
columns/columns.xlsx


+ 113 - 0
pom.xml

@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<groupId>cn.com.qmth.archive</groupId>
+	<artifactId>examcloud-archive-tool</artifactId>
+	<version>1.0.0</version>
+	<packaging>jar</packaging>
+	<parent>
+		<groupId>org.springframework.boot</groupId>
+		<artifactId>spring-boot-starter-parent</artifactId>
+		<version>2.3.12.RELEASE</version>
+		<relativePath />
+	</parent>
+    <properties>
+        <spring-boot.version>2.3.12.RELEASE</spring-boot.version>
+        <mybatis-plus.version>3.4.3.3</mybatis-plus.version>
+        <maven-compiler-version>3.8.1</maven-compiler-version>
+        <maven-surefire-version>2.22.2</maven-surefire-version>
+        <maven.compiler.source>1.8</maven.compiler.source>
+        <maven.compiler.target>1.8</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+        <qmth-boot-version>1.0.5</qmth-boot-version>
+    </properties>
+
+	<dependencies>
+		<dependency>
+            <groupId>com.qmth.boot</groupId>
+            <artifactId>starter-api</artifactId>
+            <version>${qmth-boot-version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.qmth.boot</groupId>
+            <artifactId>data-mybatis-plus</artifactId>
+            <version>${qmth-boot-version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.github.jeffreyning</groupId>
+            <artifactId>mybatisplus-plus</artifactId>
+            <version>1.5.1-RELEASE</version>
+        </dependency>
+        <dependency>
+            <groupId>com.qmth.boot</groupId>
+            <artifactId>core-concurrent</artifactId>
+            <version>${qmth-boot-version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.qmth.boot</groupId>
+            <artifactId>tools-poi</artifactId>
+            <version>${qmth-boot-version}</version>
+        </dependency>
+
+        <!-- Swagger jars start -->
+        <dependency>
+            <groupId>com.github.xiaoymin</groupId>
+            <artifactId>knife4j-spring-boot-starter</artifactId>
+            <version>2.0.9</version>
+        </dependency>
+        <dependency>
+            <groupId>io.swagger</groupId>
+            <artifactId>swagger-annotations</artifactId>
+            <version>1.5.24</version>
+        </dependency>
+        <dependency>
+            <groupId>io.swagger</groupId>
+            <artifactId>swagger-models</artifactId>
+            <version>1.5.24</version>
+        </dependency>
+        <!-- Swagger jars end -->
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+            <version>1.2.83</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-collections4</artifactId>
+            <version>4.4</version>
+        </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>2.8.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-text</artifactId>
+            <version>1.10.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-math3</artifactId>
+            <version>3.6.1</version>
+        </dependency>
+	</dependencies>
+
+	<build>
+		<finalName>${project.artifactId}</finalName>
+
+		<plugins>
+			<plugin>
+				<groupId>org.springframework.boot</groupId>
+				<artifactId>spring-boot-maven-plugin</artifactId>
+				<configuration>
+					<includeSystemScope>true</includeSystemScope>
+				</configuration>
+			</plugin>
+		</plugins>
+	</build>
+
+</project>

+ 2 - 0
sql/work.sql

@@ -0,0 +1,2 @@
+INSERT INTO `ec_m_mark_work` (`id`,`append_time`,`created_on`,`exam_id`,`is_end`,`name`,`remark`,`root_org_id`,`status`) values (38,null,'2024-07-23 09:31:16',null,0,'1','',0,1); 
+INSERT INTO `ec_m_mark_work` (`id`,`append_time`,`created_on`,`exam_id`,`is_end`,`name`,`remark`,`root_org_id`,`status`) values (69,null,'2025-05-08 14:02:19',null,0,'1','',0,1); 

+ 22 - 0
src/main/java/cn/com/qmth/archive/ArchiveApplication.java

@@ -0,0 +1,22 @@
+package cn.com.qmth.archive;
+
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+import com.github.jeffreyning.mybatisplus.conf.EnableMPP;
+
+@EnableMPP
+@SpringBootApplication
+@EnableScheduling
+@Configuration
+@MapperScan("cn.com.qmth.archive.dao")
+public class ArchiveApplication {
+
+    public static void main(String[] args) {
+        SpringApplication.run(ArchiveApplication.class, args);
+    }
+
+}

+ 8 - 0
src/main/java/cn/com/qmth/archive/bean/TableCols.java

@@ -0,0 +1,8 @@
+package cn.com.qmth.archive.bean;
+
+public class TableCols {
+
+    public static String[] work = new String[] { "id", "append_time", "created_on", "exam_id", "is_end", "name",
+            "remark", "root_org_id", "status" };
+
+}

+ 20 - 0
src/main/java/cn/com/qmth/archive/config/InitData.java

@@ -0,0 +1,20 @@
+package cn.com.qmth.archive.config;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.stereotype.Component;
+
+import cn.com.qmth.archive.service.MarkingService;
+
+@Component
+public class InitData implements CommandLineRunner {
+
+    @Autowired
+    private MarkingService MarkingService;
+
+    @Override
+    public void run(String... args) throws Exception {
+        MarkingService.exportSql();
+    }
+
+}

+ 20 - 0
src/main/java/cn/com/qmth/archive/config/SysProperty.java

@@ -0,0 +1,20 @@
+package cn.com.qmth.archive.config;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+@Component
+public class SysProperty {
+
+    @Value("${archive.workids}")
+    private String workids;
+
+    public String getWorkids() {
+        return workids;
+    }
+
+    public void setWorkids(String workids) {
+        this.workids = workids;
+    }
+
+}

+ 11 - 0
src/main/java/cn/com/qmth/archive/dao/MarkingDao.java

@@ -0,0 +1,11 @@
+package cn.com.qmth.archive.dao;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.ibatis.annotations.Param;
+
+public interface MarkingDao {
+
+    List<Map<String, Object>> getWork(@Param("cols") String[] cols, @Param("workids") List<Long> workids);
+}

+ 10 - 0
src/main/java/cn/com/qmth/archive/service/MarkingService.java

@@ -0,0 +1,10 @@
+package cn.com.qmth.archive.service;
+
+/**
+ * 类注释
+ */
+public interface MarkingService {
+
+    void exportSql();
+
+}

+ 122 - 0
src/main/java/cn/com/qmth/archive/service/impl/MarkingServiceImpl.java

@@ -0,0 +1,122 @@
+package cn.com.qmth.archive.service.impl;
+
+import java.io.File;
+import java.io.IOException;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.qmth.boot.core.exception.StatusException;
+
+import cn.com.qmth.archive.bean.TableCols;
+import cn.com.qmth.archive.config.SysProperty;
+import cn.com.qmth.archive.dao.MarkingDao;
+import cn.com.qmth.archive.service.MarkingService;
+import cn.com.qmth.archive.utils.DateUtil;
+import cn.com.qmth.archive.utils.FileUtil;
+
+@Service
+public class MarkingServiceImpl implements MarkingService {
+
+    private final static Logger log = LoggerFactory.getLogger(MarkingService.class);
+
+    @Autowired
+    private SysProperty sysProperty;
+
+    @Autowired
+    private MarkingDao markingDao;
+
+    @Override
+    public void exportSql() {
+        File dir = new File("./sql");
+        if (dir.exists()) {
+            FileUtil.deleteDirectory(dir);
+        }
+        dir.mkdirs();
+        String workidStr = sysProperty.getWorkids();
+        if (StringUtils.isBlank(workidStr)) {
+            throw new StatusException("配置文件中workids为空");
+        }
+        List<Long> workids = new ArrayList<>();
+        for (String wid : workidStr.split(",")) {
+            if (StringUtils.isBlank(wid)) {
+                throw new StatusException("配置文件中workids错误");
+            }
+            try {
+                workids.add(Long.valueOf(wid));
+            } catch (NumberFormatException e) {
+                throw new StatusException("配置文件中workids错误");
+            }
+        }
+        log.warn("*********************开始处理,workid:" + StringUtils.join(workids, ","));
+        try {
+            getWork(dir, workids);
+            log.warn("*********************处理结束,成功");
+        } catch (Exception e) {
+            throw new StatusException("*********************处理结束,失败", e);
+        }
+    }
+
+    private void getWork(File dir, List<Long> workids) throws IOException {
+        log.warn("*********************处理work");
+        List<Map<String, Object>> work = markingDao.getWork(TableCols.work, workids);
+        if (CollectionUtils.isEmpty(work)) {
+            throw new StatusException("work表没有数据");
+        }
+        File sql = new File(dir.getAbsolutePath() + "/work.sql");
+        sql.createNewFile();
+        List<String> ret = new ArrayList<>();
+        for (Map<String, Object> map : work) {
+            ret.add(getLine("ec_m_mark_work", TableCols.work, map));
+        }
+        FileUtils.writeLines(sql, "utf-8", ret, false);
+    }
+
+    private String getLine(String tbName, String[] cols, Map<String, Object> line) {
+        StringBuilder sb = new StringBuilder("INSERT INTO `" + tbName + "` (`");
+        sb.append(StringUtils.join(cols, "`,`") + "`) ");
+        sb.append("values (");
+        for (String col : cols) {
+            Object ob = line.get(col);
+            if (ob == null) {
+                sb.append("null,");
+            } else {
+                if (ob instanceof Boolean) {
+                    Boolean tem = (Boolean) ob;
+                    if (tem) {
+                        sb.append("1,");
+                    } else {
+                        sb.append("0,");
+                    }
+                } else if (ob instanceof LocalDateTime) {
+                    LocalDateTime tem = (LocalDateTime) ob;
+                    Instant instant = tem.atZone(ZoneId.systemDefault()).toInstant();
+
+                    // 将 Instant 转换为 Date
+                    Date date = Date.from(instant);
+                    sb.append("'" + DateUtil.format(date, DateUtil.DatePatterns.CHINA_DEFAULT) + "',");
+                } else if (ob instanceof String) {
+                    sb.append("'" + ob.toString() + "',");
+                } else {
+                    sb.append(ob.toString() + ",");
+                }
+            }
+        }
+        sb.deleteCharAt(sb.length() - 1);
+        sb.append("); ");
+        return sb.toString();
+    }
+
+}

+ 40 - 0
src/main/java/cn/com/qmth/archive/utils/BatchSetDataUtil.java

@@ -0,0 +1,40 @@
+package cn.com.qmth.archive.utils;
+
+import java.util.List;
+
+public abstract class BatchSetDataUtil<P> {
+
+    /**
+     * @param dataList
+     *            待填充的对象集合
+     * @param batchSize
+     *            每批数量
+     */
+    public final void setDataForBatch(List<P> dataList, int batchSize) {
+        if (dataList == null || dataList.size() == 0) {
+            return;
+        }
+        int batchIndex = 1;
+        if (dataList.size() <= batchSize) {
+            setData(dataList);
+            afterBatch(dataList, batchIndex, batchSize);
+        } else {
+            int size = dataList.size();
+            int len = batchSize;
+            int count = (size + len - 1) / len;
+
+            for (int i = 0; i < count; i++) {
+                List<P> subList = dataList.subList(i * len, ((i + 1) * len > size ? size : len * (i + 1)));
+                setData(subList);
+                afterBatch(subList, batchIndex, batchSize);
+                batchIndex++;
+            }
+        }
+    }
+
+    protected void afterBatch(List<P> dataList, int batchIndex, int batchSize) {
+
+    }
+
+    protected abstract void setData(List<P> dataList);
+}

+ 200 - 0
src/main/java/cn/com/qmth/archive/utils/Calculator.java

@@ -0,0 +1,200 @@
+package cn.com.qmth.archive.utils;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.math3.stat.StatUtils;
+import org.apache.commons.math3.stat.correlation.PearsonsCorrelation;
+
+import com.qmth.boot.core.exception.StatusException;
+
+/**
+ * 计算器
+ *
+ * @author
+ * @date 2019年7月30日
+ * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
+ */
+public class Calculator {
+    public static double mean(double[] values) {
+        if (values.length == 0) {
+            return 0;
+        }
+        return StatUtils.mean(values);
+    }
+    public static double correlation(double[] x, double[] y) {
+        if (x.length != y.length) {
+            throw new StatusException("两组样本数据数量大小不一致!");
+        }
+
+        if (x.length < 2) {
+            // throw new StatusException("样本数据数量过少!");
+            return 0d;
+        }
+
+        double r = new PearsonsCorrelation().correlation(x, y);
+        if (Double.isNaN(r)) {
+            return 0;
+        }
+        return r;
+    }
+    /**
+     * 加法 保留两位小数
+     * 
+     * @param v1
+     * @param v2
+     * @return
+     */
+    public static double add(double v1, double v2) {
+        return add(v1, v2, 2);
+
+    }
+
+    /**
+     * 加法 保留指定位小数
+     * 
+     * @param v1
+     * @param v2
+     * @param len
+     * @return
+     */
+    public static double add(double v1, double v2, int len) {
+        BigDecimal b1 = new BigDecimal(v1);
+        BigDecimal b2 = new BigDecimal(v2);
+        return b1.add(b2).setScale(len, BigDecimal.ROUND_HALF_UP).doubleValue();
+
+    }
+    
+    /** 加法 保留指定位小数
+     * @param ds
+     * @param len
+     * @return
+     */
+    public static double add(List<Double> ds, int len) {
+        if(CollectionUtils.isEmpty(ds)) {
+            throw new StatusException("数组为空");
+        }
+        BigDecimal ret = new BigDecimal(0.0);
+        for(Double d:ds) {
+            if(d==null) {
+                throw new StatusException("数组元素为空");
+            }
+            ret=ret.add(new BigDecimal(d));
+        }
+        return ret.setScale(len, BigDecimal.ROUND_HALF_UP).doubleValue();
+
+    }
+
+    /**
+     * 减法 保留两位小数
+     * 
+     * @param v1
+     * @param v2
+     * @return
+     */
+    public static double subtract(double v1, double v2) {
+        return subtract(v1, v2, 2);
+
+    }
+
+    /**
+     * 减法 保留指定位小数
+     * 
+     * @param v1
+     * @param v2
+     * @param len
+     * @return
+     */
+    public static double subtract(double v1, double v2, int len) {
+        BigDecimal b1 = new BigDecimal(v1);
+        BigDecimal b2 = new BigDecimal(v2);
+        return b1.subtract(b2).setScale(len, BigDecimal.ROUND_HALF_UP).doubleValue();
+
+    }
+    /**差值绝对值
+     * @param v1
+     * @param v2
+     * @param len
+     * @return
+     */
+    public static double absoluteDiff(double v1, double v2, int len) {
+        BigDecimal b1 = new BigDecimal(v1);
+        BigDecimal b2 = new BigDecimal(v2);
+        if(v1>v2) {
+            return b1.subtract(b2).setScale(len, BigDecimal.ROUND_HALF_UP).doubleValue();
+        }else {
+            return b2.subtract(b1).setScale(len, BigDecimal.ROUND_HALF_UP).doubleValue();
+        }
+
+    }
+    /**
+     * 乘法 保留两位小数
+     * 
+     * @param v1
+     * @param v2
+     * @return
+     */
+    public static double multiply(double v1, double v2) {
+        return multiply(v1, v2, 2);
+
+    }
+
+    /**
+     * 乘法 保留指定位小数
+     * 
+     * @param v1
+     * @param v2
+     * @param len
+     * @return
+     */
+    public static double multiply(double v1, double v2, int len) {
+        BigDecimal b1 = new BigDecimal(v1);
+        BigDecimal b2 = new BigDecimal(v2);
+        return b1.multiply(b2).setScale(len, BigDecimal.ROUND_HALF_UP).doubleValue();
+
+    }
+    
+    
+    /**
+     * 除法 保留两位小数
+     * 
+     * @param v1
+     * @param v2
+     * @param len
+     * @return
+     */
+    public static double divide(double v1, double v2) {
+        return divide(v1, v2, 2);
+    }
+
+    /**
+     * 除法 保留指定位小数
+     * 
+     * @param v1
+     * @param v2
+     * @param len
+     * @return
+     */
+    public static double divide(double v1, double v2, int len) {
+        BigDecimal b1 = new BigDecimal(v1);
+        BigDecimal b2 = new BigDecimal(v2);
+        return b1.divide(b2, len, BigDecimal.ROUND_HALF_UP).doubleValue();
+    }
+
+    public static String divide2String(Double v1, Double v2, int len) {
+        if(v1==null||v2==null||v2==0) {
+            return "-";
+        }
+        BigDecimal b1 = new BigDecimal(v1);
+        BigDecimal b2 = new BigDecimal(v2);
+        return String.valueOf(b1.divide(b2, len, BigDecimal.ROUND_HALF_UP).doubleValue());
+    }
+    
+    public static String percentage(double v1, double v2, int len) {
+        BigDecimal b1 = new BigDecimal(v1);
+        BigDecimal b2 = new BigDecimal(v2);
+        BigDecimal b3 = new BigDecimal(100);
+        return b1.divide(b2, len+2, BigDecimal.ROUND_HALF_UP).multiply(b3).setScale(len, BigDecimal.ROUND_HALF_UP).doubleValue()+"%";
+    }
+}

+ 208 - 0
src/main/java/cn/com/qmth/archive/utils/DateUtil.java

@@ -0,0 +1,208 @@
+package cn.com.qmth.archive.utils;
+
+import java.math.BigDecimal;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+
+/**
+ * 日期工具
+ * 
+ * @author
+ */
+public class DateUtil {
+
+    /**
+     * patterns describing the date and time format
+     *
+     * @author
+     */
+    public interface DatePatterns {
+
+        public static final String DEFAULT = "yyyyMMddHHmmss";
+
+        public static final String YYYY = "yyyy";
+
+        public static final String YYYYMM = "yyyyMM";
+
+        public static final String YYYYMMDD = "yyyyMMdd";
+
+        public static final String YYYYMMDDHH = "yyyyMMddHH";
+
+        public static final String YYYYMMDDHHMM = "yyyyMMddHHmm";
+
+        public static final String CHINA_DEFAULT = "yyyy-MM-dd HH:mm:ss";
+
+        public static final String YYYY_MM = "yyyy-MM";
+
+        public static final String YYYY_MM_DD = "yyyy-MM-dd";
+
+        public static final String YYYY_MM_DD_HH_MM = "yyyy-MM-dd HH:mm";
+
+        public static final String YYYY_MM_DD_HH_MM_SS_SSS = "yyyy-MM-dd HH:mm:ss.SSS";
+    }
+
+    /**
+     * get now date.
+     * 
+     * @param pattern
+     * @return
+     */
+    public static String now(String pattern) {
+        return format(new Date(), pattern);
+    }
+
+    /**
+     * get now china date.
+     * 
+     * @return
+     */
+    public static String chinaNow() {
+        return format(new Date(), DatePatterns.CHINA_DEFAULT);
+    }
+
+    /**
+     * format date.
+     * 
+     * @param date
+     * @param pattern
+     * @return
+     */
+    public static String format(Date date, String pattern) {
+        try {
+            SimpleDateFormat df = new SimpleDateFormat(pattern);
+            return df.format(date);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * format now date.
+     *
+     * @author
+     * @param pattern
+     * @return
+     */
+    public static String formatNow(String pattern) {
+        try {
+            SimpleDateFormat df = new SimpleDateFormat(pattern);
+            return df.format(new Date());
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * parse date.
+     * 
+     * @param source
+     * @param pattern
+     * @return
+     */
+    public static Date parse(String source, String pattern) {
+        SimpleDateFormat df = new SimpleDateFormat(pattern);
+        try {
+            return df.parse(source);
+        } catch (ParseException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * parse date randomly.
+     *
+     * @author
+     * @param s
+     * @return
+     */
+    public static Date parseRandomly(String s) {
+        if (s.matches("\\d{4}/\\d{1,2}/\\d{1,2}\\s+\\d{1,2}:\\d{1,2}:\\d{1,2}")) {
+            return parse(s, "yyyy/MM/dd HH:mm:ss");
+        } else if (s.matches("\\d{4}/\\d{1,2}/\\d{1,2}\\s+\\d{1,2}:\\d{1,2}")) {
+            return parse(s, "yyyy/MM/dd HH:mm");
+        } else if (s.matches("\\d{4}/\\d{1,2}/\\d{1,2}")) {
+            return parse(s, "yyyy/MM/dd");
+        } else if (s.matches("\\d{4}-\\d{1,2}-\\d{1,2}\\s+\\d{1,2}:\\d{1,2}:\\d{1,2}")) {
+            return parse(s, "yyyy-MM-dd HH:mm:ss");
+        } else if (s.matches("\\d{4}-\\d{1,2}-\\d{1,2}\\s+\\d{1,2}:\\d{1,2}")) {
+            return parse(s, "yyyy-MM-dd HH:mm");
+        } else if (s.matches("\\d{4}-\\d{1,2}-\\d{1,2}")) {
+            return parse(s, "yyyy-MM-dd");
+        } else {
+            throw new RuntimeException("unsupported date string.");
+        }
+    }
+
+    /**
+     * 解析excel日期
+     *
+     * @author
+     * @param number
+     * @return
+     */
+    public static Date parseExcel(String number) {
+
+        BigDecimal bd = new BigDecimal(number);
+        int days = bd.intValue();
+        int mills = (int) Math.round(bd.subtract(new BigDecimal(days)).doubleValue() * 24 * 3600);
+
+        Calendar c = Calendar.getInstance();
+        c.set(1900, 0, 1);
+        c.add(Calendar.DATE, days - 2);
+        int hour = mills / 3600;
+        int minute = (mills - hour * 3600) / 60;
+        int second = mills - hour * 3600 - minute * 60;
+        c.set(Calendar.HOUR_OF_DAY, hour);
+        c.set(Calendar.MINUTE, minute);
+        c.set(Calendar.SECOND, second);
+
+        Date date = c.getTime();
+
+        return date;
+    }
+
+    /**
+     * 是否同一天
+     *
+     * @author
+     * @param date1
+     * @param date2
+     * @return
+     */
+    public static boolean isSameDay(Date date1, Date date2) {
+        if (null == date1) {
+            throw new RuntimeException("first argument must not be null");
+        }
+        if (null == date2) {
+            throw new RuntimeException("second argument must not be null");
+        }
+
+        Calendar cal1 = Calendar.getInstance();
+        cal1.setTime(date1);
+        Calendar cal2 = Calendar.getInstance();
+        cal2.setTime(date2);
+        return isSameDay(cal1, cal2);
+    }
+
+    /**
+     * 是否同一天
+     *
+     * @author
+     * @param calendar1
+     * @param calendar2
+     * @return
+     */
+    public static boolean isSameDay(Calendar calendar1, Calendar calendar2) {
+        if (null == calendar1) {
+            throw new RuntimeException("first argument must not be null");
+        }
+        if (null == calendar2) {
+            throw new RuntimeException("second argument must not be null");
+        }
+        return calendar1.get(0) == calendar2.get(0) && calendar1.get(1) == calendar2.get(1)
+                && calendar1.get(6) == calendar2.get(6);
+    }
+
+}

+ 899 - 0
src/main/java/cn/com/qmth/archive/utils/FileUtil.java

@@ -0,0 +1,899 @@
+package cn.com.qmth.archive.utils;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.UUID;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+
+import sun.misc.BASE64Decoder;
+
+@SuppressWarnings("restriction")
+public class FileUtil {
+
+    public static void deleteDirectory(File dirFile) {
+        if (!dirFile.exists()) {
+            return;
+        }
+        if (dirFile.isFile()) {
+            dirFile.delete();
+        } else {
+            File[] files = dirFile.listFiles();
+            if (files != null) {
+                for (int i = 0; i < files.length; i++) {
+                    deleteDirectory(files[i]);
+                }
+            }
+            dirFile.delete();
+        }
+    }
+
+    public static boolean saveFileByUrl(String fileUrl, File file) {
+        URL url;
+        try {
+            url = new URL(fileUrl);
+        } catch (MalformedURLException e) {
+            throw new RuntimeException("文件链接错误:" + fileUrl, e);
+        }
+
+        HttpURLConnection connection;
+        try {
+            connection = (HttpURLConnection) url.openConnection();
+        } catch (IOException e) {
+            throw new RuntimeException("下载出错", e);
+        }
+
+        try (DataInputStream dataInputStream = new DataInputStream(connection.getInputStream());
+                FileOutputStream fileOutputStream = new FileOutputStream(file);
+                DataOutputStream dataOutputStream = new DataOutputStream(fileOutputStream);) {
+
+            byte[] buffer = new byte[4096];
+            int count;
+            while ((count = dataInputStream.read(buffer)) > 0) {
+                dataOutputStream.write(buffer, 0, count);
+            }
+            fileOutputStream.flush();
+            dataOutputStream.flush();
+            return true;
+        } catch (Exception e) {
+            throw new RuntimeException("下载出错", e);
+        } finally {
+            if (connection != null) {
+                connection.disconnect();
+            }
+        }
+    }
+
+    public static byte[] base64ToByte(String base64Str) {
+        if (base64Str.contains("data:image")) {// base64图片
+            base64Str = base64Str.substring(base64Str.indexOf(",") + 1).replaceAll("%0A", "");
+            BASE64Decoder decoder = new BASE64Decoder();
+            try {
+                return decoder.decodeBuffer(base64Str);
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        } else {
+            throw new RuntimeException("not base64 string");
+        }
+    }
+
+    public static void base64ToFile(File file, String base64Str) {
+        if (base64Str.contains("data:image")) {// base64图片
+            base64Str = base64Str.substring(base64Str.indexOf(",") + 1).replaceAll("%0A", "");
+            BASE64Decoder decoder = new BASE64Decoder();
+            try {
+                byte[] bytes = decoder.decodeBuffer(base64Str);
+                FileUtils.writeByteArrayToFile(file, bytes);
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        } else {
+            throw new RuntimeException("not base64 string");
+        }
+    }
+
+    public static String fileToBase64(InputStream is, String suff) {
+        byte[] base64Byte;
+        try {
+            base64Byte = new byte[0];
+            byte[] imgByte;
+            imgByte = IOUtils.toByteArray(is);
+            base64Byte = Base64.encodeBase64(imgByte);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        } finally {
+            if (is != null) {
+                try {
+                    is.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+        return "data:image/" + suff + ";base64," + new String(base64Byte);
+    }
+
+    public static String byteToBase64(byte[] imgByte, String suff) {
+        InputStream is = null;
+        byte[] base64Byte = new byte[0];
+        try {
+            base64Byte = Base64.encodeBase64(imgByte);
+        } finally {
+            if (is != null) {
+                try {
+                    is.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+        return "data:image/" + suff + ";base64," + new String(base64Byte);
+    }
+
+    public static String fileToBase64(File imgFile) {
+        String fileName = imgFile.getName();
+        String suff = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
+        InputStream is = null;
+        byte[] base64Byte;
+        try {
+            base64Byte = new byte[0];
+            byte[] imgByte;
+            is = new FileInputStream(imgFile);
+            imgByte = IOUtils.toByteArray(is);
+            base64Byte = Base64.encodeBase64(imgByte);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        } finally {
+            if (is != null) {
+                try {
+                    is.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+        return "data:image/" + suff + ";base64," + new String(base64Byte);
+    }
+
+    /**
+     * 下载服务器上的文件
+     *
+     * @param filename
+     *            文件名称
+     * @param fullFilePath
+     *            文件全路径
+     * @param response
+     */
+    public static void downloadFile(String filename, String fullFilePath, HttpServletResponse response) {
+        try (InputStream in = new FileInputStream(fullFilePath); OutputStream out = response.getOutputStream();) {
+
+            // 设置编码
+            response.setCharacterEncoding("UTF-8");
+
+            // 设置Content-Disposition,名称强制为UTF-8
+            response.setHeader("Content-Disposition", "attachment;filename="
+                    + URLEncoder.encode(filename, "UTF-8").replace("%28", "(").replace("%29", ")"));
+            response.setHeader("Accept-Length", String.valueOf(in.available()));
+
+            // 设置强制下载不打开
+            response.setContentType("application/octet-stream;charset=utf-8");
+
+            // 读取目标文件,通过response将目标文件写到客户端
+            byte[] buffer = new byte[4096];
+            int count;
+            while ((count = in.read(buffer)) > 0) {
+                out.write(buffer, 0, count);
+            }
+
+            response.flushBuffer();
+        } catch (IOException e) {
+            throw new RuntimeException("下载出错:" + e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 获得文件MIME类型
+     *
+     * @param filename
+     * @return
+     */
+    public static String getContentType(String filename) {
+        String type = null;
+        Path path = Paths.get(filename);
+        try {
+            type = Files.probeContentType(path);
+        } catch (IOException e) {
+            throw new RuntimeException("出错:" + e.getMessage(), e);
+        }
+        return type;
+    }
+
+    /**
+     * 将存放在sourceFilePath目录下的源文件,打包成fileName名称的zip文件,并存放到zipFilePath路径下
+     *
+     * @param sourceFilePath
+     *            :待压缩的文件夹路径
+     * @param zipFilePath
+     *            :压缩后zip文件的存放路径
+     * @param fileName
+     *            :zip文件的名称
+     * @return
+     */
+    public static void fileToZip(String sourceFilePath, String zipFilePath, String fileName) {
+
+        File sourceFile = new File(sourceFilePath);
+        if (!sourceFile.exists()) {
+            throw new RuntimeException("待压缩的文件目录:" + sourceFilePath + "不存在.");
+        }
+
+        File zipFile = new File(zipFilePath + File.separator + fileName + ".zip");
+        if (zipFile.exists()) {
+            throw new RuntimeException(zipFilePath + "目录下存在名字为:" + fileName + ".zip" + "打包文件.");
+        }
+
+        File[] sourceFiles = sourceFile.listFiles();
+        if (null == sourceFiles || sourceFiles.length < 1) {
+            throw new RuntimeException("待压缩的文件目录:" + sourceFilePath + "里面不存在文件,无需压缩.");
+        }
+
+        try (FileOutputStream fos = new FileOutputStream(zipFile);
+                BufferedOutputStream bos = new BufferedOutputStream(fos);
+                ZipOutputStream zos = new ZipOutputStream(bos);) {
+
+            byte[] bytes = new byte[1024 * 10];
+            for (int i = 0; i < sourceFiles.length; i++) {
+                File file = sourceFiles[i];
+                if (!file.isFile()) {
+                    continue;
+                }
+
+                try (FileInputStream fis = new FileInputStream(file);
+                        BufferedInputStream bis = new BufferedInputStream(fis, 1024 * 10);) {
+                    // 创建ZIP实体,并添加进压缩包
+                    String fileEncode = System.getProperty("file.encoding");
+                    String name = new String(file.getName().getBytes(), fileEncode);
+
+                    ZipEntry zipEntry = new ZipEntry(name);
+                    zos.putNextEntry(zipEntry);
+
+                    // 读取待压缩的文件并写进压缩包里
+                    int read;
+                    while ((read = bis.read(bytes, 0, 1024 * 10)) != -1) {
+                        zos.write(bytes, 0, read);
+                    }
+
+                    zos.flush();
+                } catch (Exception e) {
+                    throw new RuntimeException("出错:" + e.getMessage(), e);
+                }
+            }
+        } catch (Exception e) {
+            throw new RuntimeException("出错:" + e.getMessage(), e);
+        }
+    }
+
+    public static void createDirectory(String downloadDirectory) {
+        File directory = new File(downloadDirectory);
+        if (!directory.exists()) {
+            directory.mkdirs();
+        } else {
+            FileUtils.deleteQuietly(directory);
+            directory.mkdirs();
+        }
+    }
+
+    public static File createZip(String sourceFilePath, String targetFilePath) throws IOException {
+        OutputStream fos = null;
+        ZipOutputStream zos = null;
+        try {
+            File zipfile = new File(targetFilePath);
+            zipfile.deleteOnExit();
+            fos = new FileOutputStream(zipfile);
+            zos = new ZipOutputStream(fos);
+            // zos.setEncoding("utf-8"); // Solve linxu's mess
+            writeZip(new File(sourceFilePath), null, zos);
+            return zipfile;
+        } finally {
+            if (zos != null) {
+                zos.close();
+            }
+            if (fos != null) {
+                fos.close();
+            }
+        }
+    }
+
+    private static void writeZip(File file, String parentPath, ZipOutputStream zos) throws IOException {
+        if (file.exists()) {
+            ZipEntry ze = null;
+            if (file.isDirectory()) {// Processing folder
+                if (parentPath == null) {
+                    parentPath = "";
+                } else {
+                    parentPath += file.getName() + "/";
+                }
+                File[] files = file.listFiles();
+                if (files != null) {
+                    for (File f : files) {
+                        writeZip(f, parentPath, zos);
+                    }
+                } else { // An empty directory creates the current directory
+                    try {
+                        ze = new ZipEntry(parentPath);
+                        // ze.setUnixMode(755);// Solve Linux mess file Settings
+                        // 644 directory Settings 755
+                        zos.putNextEntry(ze);
+
+                        zos.flush();
+                    } finally {
+                        if (zos != null) {
+                            zos.closeEntry();
+                        }
+                    }
+                }
+            } else {
+                FileInputStream fis = null;
+                try {
+                    fis = new FileInputStream(file);
+                    ze = new ZipEntry(parentPath + file.getName());
+                    // ze.setUnixMode(644);// Solve Linux mess file Settings 644
+                    // directory Settings 755
+                    zos.putNextEntry(ze);
+                    byte[] content = new byte[1024];
+                    int len;
+                    while ((len = fis.read(content)) != -1) {
+                        zos.write(content, 0, len);
+                        zos.flush();
+                    }
+
+                } finally {
+                    if (fis != null) {
+                        fis.close();
+                    }
+                    if (zos != null) {
+                        zos.closeEntry();
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * 文件压缩
+     *
+     * @param target
+     *            目录或文件
+     * @param zipFile
+     *            压缩后的ZIP文件
+     */
+    public static boolean doZip(File target, File zipFile) {
+        if (target == null || !target.exists()) {
+            throw new RuntimeException("目录或文件不能为空!");
+        }
+
+        if (zipFile == null) {
+            throw new RuntimeException("待压缩的文件不能为空!");
+        }
+
+        try (OutputStream outStream = new FileOutputStream(zipFile);
+                ZipOutputStream zipOutStream = new ZipOutputStream(outStream, Charset.forName("UTF-8"));) {
+            if (!zipFile.exists()) {
+                boolean ok = zipFile.createNewFile();
+                if (!ok) {
+                    throw new RuntimeException("压缩的文件创建失败!");
+                }
+            }
+
+            if (target.isDirectory()) {
+                File[] files = target.listFiles();
+                if (files.length == 0) {
+                    throw new RuntimeException("文件夹内未找到任何文件!");
+                }
+
+                for (File file : files) {
+                    doZip(zipOutStream, file, null);
+                }
+            } else {
+                doZip(zipOutStream, target, null);
+            }
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+
+        return true;
+    }
+
+    private static void doZip(ZipOutputStream zipOutStream, File target, String parentDir) throws IOException {
+        // log.info("Zip:" + parentDir);
+        if (parentDir == null) {
+            parentDir = "";
+        }
+
+        if (!"".equals(parentDir) && !parentDir.endsWith(File.separator)) {
+            parentDir += File.separator;
+        }
+
+        if (target.isDirectory()) {
+            File[] files = target.listFiles();
+            if (files.length > 0) {
+                for (File file : files) {
+                    doZip(zipOutStream, file, parentDir + target.getName());
+                }
+            } else {
+                zipOutStream.putNextEntry(new ZipEntry(parentDir + target.getName()));
+                zipOutStream.closeEntry();
+            }
+        } else {
+            try (InputStream is = new FileInputStream(target);) {
+                zipOutStream.putNextEntry(new ZipEntry(parentDir + target.getName()));
+                int len;
+                byte[] bytes = new byte[1024];
+                while ((len = is.read(bytes)) > 0) {
+                    zipOutStream.write(bytes, 0, len);
+                }
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+            zipOutStream.closeEntry();
+        }
+    }
+
+    /**
+     * 解压文件
+     *
+     * @param targetDir
+     *            解压目录
+     * @param zipFile
+     *            待解压的ZIP文件
+     */
+    public static List<File> unZip(File targetDir, File zipFile) {
+        if (targetDir == null) {
+            throw new RuntimeException("解压目录不能为空!");
+        }
+
+        if (zipFile == null) {
+            throw new RuntimeException("待解压的文件不能为空!");
+        }
+
+        if (!zipFile.exists()) {
+            throw new RuntimeException("待解压的文件不存在!" + zipFile.getAbsolutePath());
+        }
+
+        String zipName = zipFile.getName().toLowerCase();
+        if (zipFile.isDirectory() || zipName.indexOf(".zip") < 0) {
+            throw new RuntimeException("待解压的文件格式错误!");
+        }
+
+        if (!targetDir.exists()) {
+            targetDir.mkdir();
+        }
+
+        List<File> result = new LinkedList<>();
+
+        try (ZipFile zip = new ZipFile(zipFile, Charset.forName("GBK"));) {
+
+            @SuppressWarnings("rawtypes")
+            Enumeration entries = zip.entries();
+            while (entries.hasMoreElements()) {
+                ZipEntry entry = (ZipEntry) entries.nextElement();
+
+                // Linux中需要替换掉路径的反斜杠
+                String entryName = (File.separator + entry.getName()).replaceAll("\\\\", "/");
+
+                String filePath = targetDir.getAbsolutePath() + entryName;
+                File target = new File(filePath);
+                if (entry.isDirectory()) {
+                    target.mkdirs();
+                } else {
+                    File dir = target.getParentFile();
+                    if (!dir.exists()) {
+                        dir.mkdirs();
+                    }
+
+                    try (OutputStream os = new FileOutputStream(target); InputStream is = zip.getInputStream(entry);) {
+                        IOUtils.copy(is, os);
+                        os.flush();
+                    } catch (IOException e) {
+                        throw new RuntimeException(e);
+                    }
+                    result.add(target);
+                }
+            }
+
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+
+        return result;
+    }
+
+    public static void unZipFiles(String zipFileName, String targetDirName) throws IOException {
+        if (!targetDirName.endsWith(File.separator)) {
+            targetDirName = targetDirName + File.separator;
+        }
+        ZipFile zipFile = null;
+        try {
+            // Create the ZipFile object from the ZIP file
+            zipFile = new ZipFile(zipFileName);
+            ZipEntry entry = null;
+            String entryName = null;
+            String descFileDir = null;
+            byte[] buf = new byte[4096];
+            int readByte = 0;
+            // Gets all entry in the ZIP file
+            @SuppressWarnings("rawtypes")
+            Enumeration enums = zipFile.entries();
+            // Go through all entry
+            while (enums.hasMoreElements()) {
+                entry = (ZipEntry) enums.nextElement();
+                // Get the name entry
+                entryName = entry.getName();
+                descFileDir = targetDirName + entryName;
+                if (entry.isDirectory()) {
+                    // If entry is a directory, create the directory
+                    // entry.setUnixMode(755);// Solve Linux mess file Settings
+                    // 644 directory Settings 755
+                    new File(descFileDir).mkdirs();
+                    continue;
+                } else {
+                    // If entry is a file, the parent directory is created
+                    // entry.setUnixMode(644);//Solve Linux mess file Settings
+                    // 644 directory Settings 755
+                    new File(descFileDir).getParentFile().mkdirs();
+                }
+                File file = new File(descFileDir);
+                // Open the file output stream
+                OutputStream os = null;
+                // Open the entry input stream from the ZipFile object
+                InputStream is = null;
+                try {
+                    os = new FileOutputStream(file);
+                    is = zipFile.getInputStream(entry);
+                    while ((readByte = is.read(buf)) != -1) {
+                        os.write(buf, 0, readByte);
+                    }
+                } finally {
+                    if (os != null)
+                        os.close();
+                    if (is != null)
+                        is.close();
+                }
+            }
+        } finally {
+            if (zipFile != null)
+                zipFile.close();
+        }
+    }
+
+    public static void deleteFolder(String path) {
+
+        File file = new File(path);
+        if (file.exists()) {
+            if (file.isFile()) {
+                deleteFile(path);
+            } else {
+                deleteDirectory(path);
+            }
+        }
+    }
+
+    public static void deleteFile(String path) {
+        File file = new File(path);
+        if (file.isFile() && file.exists()) {
+            file.delete();
+        }
+    }
+
+    public static void deleteDirectory(String path) {
+        if (!path.endsWith(File.separator)) {
+            path = path + File.separator;
+        }
+        File dirFile = new File(path);
+        if (!dirFile.exists() || !dirFile.isDirectory()) {
+            return;
+        }
+        File[] files = dirFile.listFiles();
+        if (files != null) {
+            for (int i = 0; i < files.length; i++) {
+                if (files[i].isFile()) {
+                    deleteFile(files[i].getAbsolutePath());
+                } else {
+                    deleteDirectory(files[i].getAbsolutePath());
+                }
+            }
+        }
+
+        dirFile.delete();
+    }
+
+    public static File cutFile(String sourcePath, String targetPath, int n) {
+        File file = new File(sourcePath);
+        File newFile = new File(targetPath);
+
+        try (FileInputStream fis = new FileInputStream(file);
+                InputStream is = new BufferedInputStream(fis);
+                OutputStream os = new FileOutputStream(newFile);) {
+
+            // 从n个字节开始读,注意中文是两个字节
+            fis.skip(n);
+
+            // 指定文件位置读取的文件流,存入新文件
+            byte buffer[] = new byte[4 * 1024];
+            int len;
+            while ((len = is.read(buffer)) != -1) {
+                os.write(buffer, 0, len);
+            }
+
+            os.flush();
+            return newFile;
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 读取文件前面部分N个字节
+     *
+     * @param path
+     *            文件路径
+     * @param headerSize
+     *            头信息字节数(必须2的倍数)
+     * @param signSize
+     *            签名信息字节数
+     * @return
+     */
+    public static String[] readFileHeader(String path, int headerSize, int signSize) {
+        int n = headerSize / 2;
+        String[] codes = new String[n + 1];
+
+        File file = new File(path);
+        try (FileInputStream fis = new FileInputStream(file); DataInputStream ois = new DataInputStream(fis);) {
+            // 分n次读取文件(n * 2)个字节
+            for (int i = 0; i < n; i++) {
+                codes[i] = String.valueOf(ois.readShort());
+            }
+
+            if (signSize > 0) {
+                StringBuilder ss = new StringBuilder();
+                for (int i = 0; i < signSize; i++) {
+                    ss.append((char) ois.readByte());
+                }
+                codes[2] = ss.toString();
+            }
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+
+        return codes;
+    }
+
+    /**
+     * 读取文件内容
+     *
+     * @param file
+     * @return
+     */
+    @SuppressWarnings("deprecation")
+    public static String readFileContent(File file) {
+        StringBuilder content = new StringBuilder();
+        InputStreamReader streamReader = null;
+        BufferedReader bufferedReader = null;
+        try {
+            String encoding = "UTF-8";
+            if (file.exists() && file.isFile()) {
+                streamReader = new InputStreamReader(new FileInputStream(file), encoding);
+                bufferedReader = new BufferedReader(streamReader);
+                String line;
+                while ((line = bufferedReader.readLine()) != null) {
+                    content.append(line);
+                }
+            }
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        } finally {
+            IOUtils.closeQuietly(streamReader);
+            IOUtils.closeQuietly(bufferedReader);
+        }
+        return content.toString();
+    }
+
+    @SuppressWarnings("deprecation")
+    public static String readFileContent(InputStream in) {
+        StringBuilder content = new StringBuilder();
+        InputStreamReader streamReader = null;
+        BufferedReader bufferedReader = null;
+        try {
+            String encoding = "UTF-8";
+            streamReader = new InputStreamReader(in, encoding);
+            bufferedReader = new BufferedReader(streamReader);
+            String line;
+            while ((line = bufferedReader.readLine()) != null) {
+                content.append(line);
+            }
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        } finally {
+            IOUtils.closeQuietly(in);
+            IOUtils.closeQuietly(streamReader);
+            IOUtils.closeQuietly(bufferedReader);
+        }
+        return content.toString();
+    }
+
+    /**
+     * 生成日期目录路径
+     */
+    public static String generateDateDir() {
+        return "/" + new SimpleDateFormat("yyyy-MM-dd").format(new Date()) + "/";
+    }
+
+    public static String generateFileName() {
+        return UUID.randomUUID().toString().replaceAll("-", "");
+    }
+
+    public static String generateDateName() {
+        return new SimpleDateFormat("yyMMddHHmmss").format(new Date());
+    }
+
+    /**
+     * 获取文件后缀名(包含".")
+     */
+    public static String getFileSuffix(String fileName) {
+        if (fileName == null) {
+            return "";
+        }
+        int index = fileName.lastIndexOf(".");
+        if (index > -1) {
+            return fileName.substring(index).toLowerCase();
+        }
+        return "";
+    }
+
+    /**
+     * 获取无后缀的文件名
+     *
+     * @param fileName
+     *            示例:../xxx/abc.xx
+     * @return 示例:../xxx/abc
+     */
+    public static String getFilePathName(String fileName) {
+        if (fileName != null && fileName.length() > 0) {
+            int index = fileName.lastIndexOf(".");
+            if (index != -1) {
+                return fileName.substring(0, index);
+            }
+        }
+        return "";
+    }
+
+    /**
+     * 创建文件目录
+     */
+    public static boolean makeDirs(String path) {
+        if (path == null || "".equals(path)) {
+            return false;
+        }
+        File folder = new File(path);
+        if (!folder.exists()) {
+            return folder.mkdirs();
+        }
+        return true;
+    }
+
+    public static void dozip(File target, File[] files) {
+        // 压缩后的 zip 文件名
+        if (target == null || !target.exists()) {
+            throw new RuntimeException("目录或文件不能为空!");
+        }
+        try {
+            // 创建 ZipOutputStream 对象
+            FileOutputStream fos = new FileOutputStream(target);
+            ZipOutputStream zipOut = new ZipOutputStream(fos, Charset.forName("UTF-8"));
+
+            // 循环每个文件并将其添加到压缩包
+            for (File vo : files) {
+                FileInputStream in = new FileInputStream(vo);
+                try {
+                    ZipEntry zipEntry = new ZipEntry(vo.getName());
+                    zipOut.putNextEntry(zipEntry);
+                    // 将文件内容写入 ZipOutputStream
+                    byte[] bytes = new byte[1024];
+                    int length;
+                    while ((length = in.read(bytes)) >= 0) {
+                        zipOut.write(bytes, 0, length);
+                    }
+                } finally {
+                    // 关闭当前文件的输入流
+                    in.close();
+                }
+            }
+
+            // 关闭 ZipOutputStream
+            zipOut.close();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static void dozip(File target, List<File> files) {
+        // 压缩后的 zip 文件名
+        if (target == null || !target.exists()) {
+            throw new RuntimeException("目录或文件不能为空!");
+        }
+        try {
+            // 创建 ZipOutputStream 对象
+            FileOutputStream fos = new FileOutputStream(target);
+            ZipOutputStream zipOut = new ZipOutputStream(fos, Charset.forName("UTF-8"));
+
+            // 循环每个文件并将其添加到压缩包
+            for (File vo : files) {
+                FileInputStream in = new FileInputStream(vo);
+                try {
+                    ZipEntry zipEntry = new ZipEntry(vo.getName());
+                    zipOut.putNextEntry(zipEntry);
+                    // 将文件内容写入 ZipOutputStream
+                    byte[] bytes = new byte[1024];
+                    int length;
+                    while ((length = in.read(bytes)) >= 0) {
+                        zipOut.write(bytes, 0, length);
+                    }
+                } finally {
+                    // 关闭当前文件的输入流
+                    in.close();
+                }
+            }
+
+            // 关闭 ZipOutputStream
+            zipOut.close();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    public static void donwLoadFile(HttpServletResponse response, String fileName, InputStream in) {
+        OutputStream out = null;
+        try {
+            fileName = URLEncoder.encode(fileName, "UTF-8");
+            response.reset();
+            response.setHeader("Content-Disposition", "inline; filename=" + fileName);
+            response.setContentType("application/octet-stream;charset=UTF-8");
+            out = new BufferedOutputStream(response.getOutputStream());
+            IOUtils.copy(in, out);
+            out.flush();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        } finally {
+            IOUtils.closeQuietly(out);
+            IOUtils.closeQuietly(in);
+        }
+    }
+}

+ 51 - 0
src/main/java/cn/com/qmth/archive/utils/FormFilePart.java

@@ -0,0 +1,51 @@
+package cn.com.qmth.archive.utils;
+
+import java.io.File;
+
+public class FormFilePart {
+
+    private String paramName;
+
+    private String filename;
+
+    private File file;
+
+    /**
+     * 构造函数
+     *
+     * @param paramName
+     * @param filename
+     * @param file
+     */
+    public FormFilePart(String paramName, String filename, File file) {
+        super();
+        this.paramName = paramName;
+        this.filename = filename;
+        this.file = file;
+    }
+
+    public String getParamName() {
+        return paramName;
+    }
+
+    public void setParamName(String paramName) {
+        this.paramName = paramName;
+    }
+
+    public String getFilename() {
+        return filename;
+    }
+
+    public void setFilename(String filename) {
+        this.filename = filename;
+    }
+
+    public File getFile() {
+        return file;
+    }
+
+    public void setFile(File file) {
+        this.file = file;
+    }
+
+}

+ 108 - 0
src/main/java/cn/com/qmth/archive/utils/HttpMethod.java

@@ -0,0 +1,108 @@
+package cn.com.qmth.archive.utils;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class HttpMethod {
+
+    public static final HttpMethod OPTIONS = new HttpMethod("OPTIONS");
+
+    public static final HttpMethod GET = new HttpMethod("GET");
+
+    public static final HttpMethod HEAD = new HttpMethod("HEAD");
+
+    public static final HttpMethod POST = new HttpMethod("POST");
+
+    public static final HttpMethod PUT = new HttpMethod("PUT");
+
+    public static final HttpMethod PATCH = new HttpMethod("PATCH");
+
+    public static final HttpMethod DELETE = new HttpMethod("DELETE");
+
+    public static final HttpMethod TRACE = new HttpMethod("TRACE");
+
+    public static final HttpMethod CONNECT = new HttpMethod("CONNECT");
+
+    private static final Map<String, HttpMethod> METHOD_MAP = new HashMap<String, HttpMethod>();
+
+    static {
+        METHOD_MAP.put(OPTIONS.toString(), OPTIONS);
+        METHOD_MAP.put(GET.toString(), GET);
+        METHOD_MAP.put(HEAD.toString(), HEAD);
+        METHOD_MAP.put(POST.toString(), POST);
+        METHOD_MAP.put(PUT.toString(), PUT);
+        METHOD_MAP.put(PATCH.toString(), PATCH);
+        METHOD_MAP.put(DELETE.toString(), DELETE);
+        METHOD_MAP.put(TRACE.toString(), TRACE);
+        METHOD_MAP.put(CONNECT.toString(), CONNECT);
+    }
+
+    private final String name;
+
+    /**
+     * 构造函数
+     *
+     * @param name
+     */
+    private HttpMethod(String name) {
+        if (name == null) {
+            throw new NullPointerException("name");
+        }
+
+        name = name.trim();
+        if (name.length() == 0) {
+            throw new IllegalArgumentException("empty name");
+        }
+
+        for (int i = 0; i < name.length(); i++) {
+            if (Character.isISOControl(name.charAt(i)) || Character.isWhitespace(name.charAt(i))) {
+                throw new IllegalArgumentException("invalid character in name");
+            }
+        }
+
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public static HttpMethod valueOf(String name) {
+        if (name == null) {
+            throw new NullPointerException("name");
+        }
+
+        name = name.trim();
+        if (name.length() == 0) {
+            throw new IllegalArgumentException("empty name");
+        }
+
+        HttpMethod result = METHOD_MAP.get(name);
+        if (result != null) {
+            return result;
+        } else {
+            throw new IllegalArgumentException("undefined name");
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return getName().hashCode();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (!(o instanceof HttpMethod)) {
+            return false;
+        }
+
+        HttpMethod that = (HttpMethod) o;
+        return getName().equals(that.getName());
+    }
+
+    @Override
+    public String toString() {
+        return getName();
+    }
+
+}

+ 376 - 0
src/main/java/cn/com/qmth/archive/utils/ImageUtil.java

@@ -0,0 +1,376 @@
+package cn.com.qmth.archive.utils;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.awt.geom.AffineTransform;
+import java.awt.image.AffineTransformOp;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+
+import javax.imageio.IIOImage;
+import javax.imageio.ImageIO;
+import javax.imageio.ImageWriteParam;
+import javax.imageio.ImageWriter;
+import javax.imageio.plugins.jpeg.JPEGImageWriteParam;
+
+public final class ImageUtil {
+
+	/**
+	 * 图片水印
+	 * 
+	 * @param pressImg  水印图片
+	 * @param targetImg 目标图片
+	 * @param x         修正值 默认在中间
+	 * @param y         修正值 默认在中间
+	 * @param alpha     透明度
+	 */
+	public final static void pressImage(String pressImg, String targetImg, int x, int y, float alpha) {
+		try {
+			File img = new File(targetImg);
+			Image src = ImageIO.read(img);
+			int wideth = src.getWidth(null);
+			int height = src.getHeight(null);
+			BufferedImage image = new BufferedImage(wideth, height, BufferedImage.TYPE_INT_RGB);
+			Graphics2D g = image.createGraphics();
+			g.drawImage(src, 0, 0, wideth, height, null);
+			// 水印文件
+			Image src_biao = ImageIO.read(new File(pressImg));
+			int wideth_biao = src_biao.getWidth(null);
+			int height_biao = src_biao.getHeight(null);
+			g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));
+			g.drawImage(src_biao, (wideth - wideth_biao) / 2, (height - height_biao) / 2, wideth_biao, height_biao,
+					null);
+			// 水印文件结束
+			g.dispose();
+			ImageIO.write((BufferedImage) image, "jpg", img);
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+	}
+
+	/**
+	 * 文字水印
+	 * 
+	 * @param pressText 水印文字
+	 * @param targetImg 目标图片
+	 * @param fontName  字体名称
+	 * @param fontStyle 字体样式
+	 * @param color     字体颜色
+	 * @param fontSize  字体大小
+	 * @param x         修正值
+	 * @param y         修正值
+	 * @param alpha     透明度
+	 */
+	public static void pressText(String pressText, String targetImg, String fontName, int fontStyle, Color color,
+			int fontSize, int x, int y, float alpha) {
+		try {
+			File img = new File(targetImg);
+			Image src = ImageIO.read(img);
+			int width = src.getWidth(null);
+			int height = src.getHeight(null);
+			BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
+			Graphics2D g = image.createGraphics();
+			g.drawImage(src, 0, 0, width, height, null);
+			g.setColor(color);
+			g.setFont(new Font(fontName, fontStyle, fontSize));
+			g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));
+			g.drawString(pressText, (width - (getLength(pressText) * fontSize)) / 2 + x, (height - fontSize) / 2 + y);
+			g.dispose();
+			ImageIO.write((BufferedImage) image, "jpg", img);
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+	}
+
+	/**
+	 * 缩放
+	 * 
+	 * @param filePath    图片路径
+	 * @param newFileName 缩放完比图片路径
+	 * @param height      高度
+	 * @param width       宽度
+	 * @param bb          比例不对时是否需要补白
+	 */
+	@SuppressWarnings("static-access")
+	public static void resize(String filePath, String newFileName, int height, int width, String formatName,
+			boolean bb) {
+		try {
+			double ratio = 0.0; // 缩放比例
+			File f = new File(filePath);
+			BufferedImage bi = ImageIO.read(f);
+			Image itemp = bi.getScaledInstance(width, height, bi.SCALE_SMOOTH);
+			// 计算比例
+			if ((bi.getHeight() > height) || (bi.getWidth() > width)) {
+				if (bi.getHeight() > bi.getWidth()) {
+					ratio = (new Integer(height)).doubleValue() / bi.getHeight();
+				} else {
+					ratio = (new Integer(width)).doubleValue() / bi.getWidth();
+				}
+				AffineTransformOp op = new AffineTransformOp(AffineTransform.getScaleInstance(ratio, ratio), null);
+				itemp = op.filter(bi, null);
+			}
+			if (bb) {
+				BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
+				Graphics2D g = image.createGraphics();
+				g.setColor(Color.white);
+				g.fillRect(0, 0, width, height);
+				if (width == itemp.getWidth(null))
+					g.drawImage(itemp, 0, (height - itemp.getHeight(null)) / 2, itemp.getWidth(null),
+							itemp.getHeight(null), Color.white, null);
+				else
+					g.drawImage(itemp, (width - itemp.getWidth(null)) / 2, 0, itemp.getWidth(null),
+							itemp.getHeight(null), Color.white, null);
+				g.dispose();
+				itemp = image;
+			}
+			BufferedImage outbi = null;
+			if (itemp instanceof BufferedImage)
+				outbi = (BufferedImage) itemp;
+			else
+				outbi = convertImageToBuffer(itemp);
+
+			File newFile = null;
+			if (newFileName == null || newFileName.length() == 0) {
+				newFile = f;
+			} else {
+				newFile = new File(newFileName);
+				if (!newFile.getParentFile().exists())
+					newFile.getParentFile().mkdirs();
+			}
+			ImageIO.write(outbi, formatName, newFile);
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+	}
+
+	/**
+	 * 对流缩放
+	 * 
+	 * @param input      图片流
+	 * @param formatName 图片格式
+	 * @param height     高度
+	 * @param width      宽度
+	 * @param bb         比例不对时是否需要补白
+	 */
+	@SuppressWarnings("static-access")
+	public static InputStream MemoryResize(InputStream input, String formatName, int height, int width, boolean bb) {
+		try {
+			double ratio = 0.0; // 缩放比例
+			BufferedImage bi = ImageIO.read(input);
+			Image itemp = bi.getScaledInstance(width, height, bi.SCALE_SMOOTH);
+			// 计算比例
+			if ((bi.getHeight() > height) || (bi.getWidth() > width)) {
+				if (bi.getHeight() > bi.getWidth()) {
+					ratio = (new Integer(height)).doubleValue() / bi.getHeight();
+				} else {
+					ratio = (new Integer(width)).doubleValue() / bi.getWidth();
+				}
+				AffineTransformOp op = new AffineTransformOp(AffineTransform.getScaleInstance(ratio, ratio), null);
+				itemp = op.filter(bi, null);
+			}
+			if (bb) {
+				BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
+				Graphics2D g = image.createGraphics();
+				g.setColor(Color.white);
+				g.fillRect(0, 0, width, height);
+				if (width == itemp.getWidth(null))
+					g.drawImage(itemp, 0, (height - itemp.getHeight(null)) / 2, itemp.getWidth(null),
+							itemp.getHeight(null), Color.white, null);
+				else
+					g.drawImage(itemp, (width - itemp.getWidth(null)) / 2, 0, itemp.getWidth(null),
+							itemp.getHeight(null), Color.white, null);
+				g.dispose();
+				itemp = image;
+			}
+			ByteArrayOutputStream output = new ByteArrayOutputStream();
+
+			BufferedImage outbi = null;
+			if (itemp instanceof BufferedImage)
+				outbi = (BufferedImage) itemp;
+			else
+				outbi = convertImageToBuffer(itemp);
+
+			ImageIO.write(outbi, formatName, output);
+			byte[] buff = output.toByteArray();
+			return new ByteArrayInputStream(buff);
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		return null;
+	}
+
+
+	public static byte[] cutImage(byte[] img, String suff, int x,int y, int width, int height) {
+		InputStream in = null;
+		BufferedImage bi = null;
+		ByteArrayOutputStream output = null;
+		try {
+			in = new ByteArrayInputStream(img);
+			bi = ImageIO.read(in);
+			height = Math.min(height, bi.getHeight());
+			width = Math.min(width, bi.getWidth());
+			if (height <= 0)
+				height = bi.getHeight();
+			if (width <= 0)
+				width = bi.getWidth();
+			x = Math.min(Math.max(0, x), bi.getWidth() - width);
+			y = Math.min(Math.max(0, y), bi.getHeight() - height);
+
+			BufferedImage bi_cropper = bi.getSubimage(x, y, width, height);
+			output = new ByteArrayOutputStream();
+			ImageIO.write(bi_cropper, suff, output);
+			byte[] ret = output.toByteArray();
+			return ret;
+		} catch (Exception e) {
+			throw new RuntimeException(e);
+		} finally {
+			try {
+				if (in != null) {
+					in.close();
+				}
+			} catch (IOException e) {
+			}
+			try {
+				if (output != null) {
+					output.close();
+				}
+			} catch (IOException e) {
+			}
+		}
+	}
+
+	public static int getLength(String text) {
+		int length = 0;
+		for (int i = 0; i < text.length(); i++) {
+			if (new String(text.charAt(i) + "").getBytes().length > 1) {
+				length += 2;
+			} else {
+				length += 1;
+			}
+		}
+		return length / 2;
+	}
+
+	public static String getExtension(File f) {
+		return (f != null) ? getExtension(f.getName()) : "";
+	}
+
+	public static String getExtension(String filename) {
+		return getExtension(filename, "");
+	}
+
+	public static String getExtension(String filename, String defExt) {
+		if ((filename != null) && (filename.length() > 0)) {
+			int i = filename.lastIndexOf('.');
+
+			if ((i > -1) && (i < (filename.length() - 1))) {
+				return filename.substring(i + 1);
+			}
+		}
+		return defExt;
+	}
+
+	public static String trimExtension(String filename) {
+		if ((filename != null) && (filename.length() > 0)) {
+			int i = filename.lastIndexOf('.');
+			if ((i > -1) && (i < (filename.length()))) {
+				return filename.substring(0, i);
+			}
+		}
+		return filename;
+	}
+
+	/**
+	 * 转化Image 为 BufferedImage
+	 * 
+	 * @param img
+	 * @return
+	 */
+	private static BufferedImage convertImageToBuffer(Image img) {
+		BufferedImage bufferedImage = new BufferedImage(img.getWidth(null), img.getHeight(null),
+				BufferedImage.TYPE_INT_RGB);
+		Graphics g = bufferedImage.createGraphics();
+		g.drawImage(img, 0, 0, null);
+		g.dispose();
+		return bufferedImage;
+	}
+
+	// 拼接图片
+	public static byte[] joinImages(List<byte[]> slices, String suff) {
+		boolean horizontal = false;
+		List<BufferedImage> imageList = new ArrayList<>();
+		for (byte[] slice : slices) {
+			InputStream in = null;
+			try {
+				in = new ByteArrayInputStream(slice);
+				BufferedImage image = ImageIO.read(in);
+				imageList.add(image);
+			} catch (Exception e) {
+				throw new RuntimeException(e);
+			} finally {
+				try {
+					if (in != null) {
+						in.close();
+					}
+				} catch (IOException e) {
+				}
+			}
+		}
+		int height = imageList.get(0).getHeight();
+		int width = imageList.get(0).getWidth();
+		if (horizontal) {
+			height = imageList.stream().mapToInt(BufferedImage::getHeight).max().getAsInt();
+			width = imageList.stream().mapToInt(BufferedImage::getWidth).sum();
+		} else {
+			width = imageList.stream().mapToInt(BufferedImage::getWidth).max().getAsInt();
+			height = imageList.stream().mapToInt(BufferedImage::getHeight).sum();
+		}
+		// 创建拼接后的图片画布,参数分别为宽,高,类型,这里我们使用RGB3元色类型
+		BufferedImage resultImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
+		Graphics graphics = resultImage.getGraphics();
+		int previousWidth = 0;
+		int previousHeight = 0;
+		for (BufferedImage image : imageList) {
+			// 向画布上画图片
+			graphics.drawImage(image, previousWidth, previousHeight, null);
+			if (horizontal) {
+				previousWidth += image.getWidth();
+			} else {
+				previousHeight += image.getHeight();
+			}
+		}
+		ByteArrayOutputStream output = new ByteArrayOutputStream();
+		try {
+			Iterator<ImageWriter> iter = ImageIO.getImageWritersByFormatName(suff);
+			ImageWriter imageWriter = iter.next();
+			JPEGImageWriteParam jpegParams = new JPEGImageWriteParam(Locale.getDefault());
+			jpegParams.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
+			jpegParams.setCompressionQuality(1.0F);
+			imageWriter.setOutput(ImageIO.createImageOutputStream(output));
+			imageWriter.write(null, new IIOImage(resultImage, null, null), jpegParams);
+			byte[] ret = output.toByteArray();
+			return ret;
+		} catch (Exception e) {
+			throw new RuntimeException(e);
+		} finally {
+			try {
+				if (output != null) {
+					output.close();
+				}
+			} catch (IOException e) {
+			}
+		}
+	}
+}

+ 96 - 0
src/main/java/cn/com/qmth/archive/utils/JsonHelper.java

@@ -0,0 +1,96 @@
+package cn.com.qmth.archive.utils;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.qmth.boot.core.exception.StatusException;
+
+public class JsonHelper {
+
+    private static final Logger log = LoggerFactory.getLogger(JsonHelper.class);
+
+    public static String toJson(Object object) {
+        if (object == null) {
+            return null;
+        }
+
+        try {
+            ObjectMapper mapper = new ObjectMapper();
+            mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
+            return mapper.writeValueAsString(object);
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            throw new StatusException("JSON转换失败!");
+        }
+    }
+
+    public static <T> T toObj(String jsonStr, Class<T> clazz) {
+        if (StringUtils.isEmpty(jsonStr)) {
+            return null;
+        }
+
+        try {
+            ObjectMapper mapper = new ObjectMapper();
+            mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
+            return mapper.readValue(jsonStr, clazz);
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            throw new StatusException("JSON转换失败!");
+        }
+    }
+
+    public static <T> List<T> toList(String jsonStr, Class<T> clazz) {
+        if (StringUtils.isEmpty(jsonStr)) {
+            return null;
+        }
+
+        try {
+            ObjectMapper mapper = new ObjectMapper();
+            mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
+            JavaType javaType = mapper.getTypeFactory().constructCollectionType(List.class, clazz);
+            return mapper.readValue(jsonStr, javaType);
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            throw new StatusException("JSON转换失败!");
+        }
+    }
+
+    public static <T> Map<String, T> toMap(String jsonStr, Class<T> clazz) {
+        if (StringUtils.isEmpty(jsonStr)) {
+            return null;
+        }
+
+        try {
+            ObjectMapper mapper = new ObjectMapper();
+            JavaType javaType = mapper.getTypeFactory().constructMapType(HashMap.class, String.class, clazz);
+            return mapper.readValue(jsonStr, javaType);
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            throw new StatusException("JSON转换失败!");
+        }
+    }
+
+    public static JsonNode getNode(String jsonStr) {
+        if (StringUtils.isEmpty(jsonStr)) {
+            return null;
+        }
+
+        try {
+            ObjectMapper mapper = new ObjectMapper();
+            return mapper.readTree(jsonStr);
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            throw new StatusException("JSON转换失败!");
+        }
+    }
+
+}

+ 88 - 0
src/main/java/cn/com/qmth/archive/utils/MD5Util.java

@@ -0,0 +1,88 @@
+package cn.com.qmth.archive.utils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Optional;
+import java.util.StringJoiner;
+
+import org.apache.commons.codec.digest.DigestUtils;
+
+/**
+ * @Description: MD5加密工具类
+ */
+public class MD5Util {
+
+    /**
+     * MD5加密
+     *
+     * @param text
+     * @return
+     * @throws Exception
+     */
+    public static String encoder(String text) throws NoSuchAlgorithmException {
+        text = Optional.of(text).get();
+        MessageDigest digest = MessageDigest.getInstance("MD5");
+        digest.update(text.getBytes(Charset.forName("UTF-8")));
+        byte s[] = digest.digest();
+        StringJoiner result = new StringJoiner("");
+        for (int i = 0; i < s.length; i++) {
+            result.add(Integer.toHexString((0x000000FF & s[i]) | 0xFFFFFF00).substring(6));
+        }
+        return result.toString();
+    }
+
+    /**
+     * MD5校验
+     *
+     * @param text
+     * @param md5
+     * @return
+     * @throws Exception
+     */
+    public static boolean verify(String text, String md5) throws NoSuchAlgorithmException {
+        text = Optional.of(text).get();
+        md5 = Optional.of(md5).get();
+        //根据传入的密钥进行验证
+        String md5Text = encoder(text);
+        if (md5Text.equalsIgnoreCase(md5)) {
+            return true;
+        }
+        return false;
+    }
+    
+	public static String md5Hex(File file) {
+		FileInputStream in = null;
+		try {
+			in = new FileInputStream(file);
+			return DigestUtils.md5Hex(in);
+		} catch (Exception e) {
+			throw new RuntimeException(e);
+		} finally {
+			if(in!=null)
+				try {
+					in.close();
+				} catch (IOException e) {
+				}
+		}
+	}
+	
+	public static String md5Hex(InputStream in) {
+		try {
+			return DigestUtils.md5Hex(in);
+		} catch (Exception e) {
+			throw new RuntimeException(e);
+		} finally {
+			if(in!=null)
+				try {
+					in.close();
+				} catch (IOException e) {
+				}
+		}
+	}
+}
+

+ 277 - 0
src/main/java/cn/com/qmth/archive/utils/OKHttpUtil.java

@@ -0,0 +1,277 @@
+package cn.com.qmth.archive.utils;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.collections4.CollectionUtils;
+
+import okhttp3.FormBody;
+import okhttp3.MediaType;
+import okhttp3.MultipartBody;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Request.Builder;
+import okhttp3.RequestBody;
+import okhttp3.Response;
+
+public class OKHttpUtil {
+
+    public static final class MediaTypes {
+
+        public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
+    }
+
+    public static interface RequestBodyBuilder {
+
+        RequestBody build();
+    }
+
+    /**
+     * json请求体构建器
+     *
+     * @author
+     * @date 2019年4月10日
+     * @Copyright (c) 2018-2020 [QQ:522080330] All Rights Reserved.
+     */
+    public static final class JsonBodyBuilder implements RequestBodyBuilder {
+
+        private String json;
+
+        public JsonBodyBuilder(String json) {
+            super();
+            this.json = json;
+        }
+
+        @Override
+        public RequestBody build() {
+            return RequestBody.create(MediaTypes.JSON, json);
+        }
+
+        @Override
+        public String toString() {
+            return json;
+        }
+    }
+
+    private static OkHttpClient okHttpClient;
+
+    // public static void initOkHttpClient() {
+    static {
+        okHttpClient = new OkHttpClient.Builder()
+                .sslSocketFactory(SSLSocketClient.getSSLSocketFactory(), SSLSocketClient.getX509TrustManager())
+                .hostnameVerifier(SSLSocketClient.getHostnameVerifier()).readTimeout(2000, TimeUnit.SECONDS)
+                .writeTimeout(2000, TimeUnit.SECONDS).build();
+    }
+
+    public static OkHttpClient getOkHttpClient() {
+        return okHttpClient;
+    }
+
+    public static void close() {
+        okHttpClient.connectionPool().evictAll();
+    }
+
+    /**
+     * 发送请求 (带json请求体)
+     *
+     * @author
+     * @param httpMethod
+     * @param url
+     * @param headers
+     * @param jsonBody
+     * @return
+     */
+    public static Response call(HttpMethod httpMethod, String url, Map<String, String> headers, String jsonBody) {
+        return call(httpMethod, url, headers, new JsonBodyBuilder(jsonBody));
+    }
+
+    /**
+     * 发送请求 (带请求体)
+     *
+     * @author
+     * @param httpMethod
+     * @param url
+     * @param headers
+     * @param requestBodyBuilder
+     * @return
+     */
+    public static Response call(HttpMethod httpMethod, String url, Map<String, String> headers,
+            RequestBodyBuilder requestBodyBuilder) {
+
+        Builder builder = null;
+        if (httpMethod.equals(HttpMethod.POST)) {
+            builder = new Request.Builder().url(url).post(requestBodyBuilder.build());
+        } else if (httpMethod.equals(HttpMethod.PUT)) {
+            builder = new Request.Builder().url(url).put(requestBodyBuilder.build());
+        } else if (httpMethod.equals(HttpMethod.DELETE)) {
+            builder = new Request.Builder().url(url).delete(requestBodyBuilder.build());
+        }
+
+        if (null != headers && 0 != headers.size()) {
+            for (Entry<String, String> entry : headers.entrySet()) {
+                builder.addHeader(entry.getKey(), entry.getValue());
+            }
+        }
+
+        Request request = builder.build();
+
+        Response response = null;
+        try {
+            response = okHttpClient.newCall(request).execute();
+            return response;
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 发送请求
+     *
+     * @author
+     * @param httpMethod
+     * @param url
+     * @return
+     */
+    public static Response call(HttpMethod httpMethod, String url) {
+        return call(httpMethod, url, null);
+    }
+
+    /**
+     * 发送请求
+     *
+     * @author
+     * @param httpMethod
+     * @param url
+     * @param headers
+     * @return
+     */
+    public static Response call(HttpMethod httpMethod, String url, Map<String, String> headers) {
+
+        Builder builder = null;
+        if (httpMethod.equals(HttpMethod.GET)) {
+            builder = new Request.Builder().url(url);
+        } else if (httpMethod.equals(HttpMethod.POST)) {
+            builder = new Request.Builder().url(url).post(new FormBody.Builder().build());
+        } else if (httpMethod.equals(HttpMethod.PUT)) {
+            builder = new Request.Builder().url(url).put(new FormBody.Builder().build());
+        } else if (httpMethod.equals(HttpMethod.DELETE)) {
+            builder = new Request.Builder().url(url).delete(new FormBody.Builder().build());
+        }
+
+        if (null != headers && 0 != headers.size()) {
+            for (Entry<String, String> entry : headers.entrySet()) {
+                builder.addHeader(entry.getKey(), entry.getValue());
+            }
+        }
+
+        Request request = builder.build();
+
+        Response response = null;
+        try {
+            response = okHttpClient.newCall(request).execute();
+            return response;
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 发送请求 (表单)
+     *
+     * @author
+     * @param httpMethod
+     * @param url
+     * @param headers
+     * @param params
+     * @return
+     */
+    public static Response call(HttpMethod httpMethod, String url, Map<String, String> headers,
+            Map<String, String> params) {
+
+        okhttp3.FormBody.Builder formBody = new FormBody.Builder();
+
+        if (null != params && 0 != params.size()) {
+            for (Entry<String, String> entry : params.entrySet()) {
+                formBody.add(entry.getKey(), entry.getValue());
+            }
+        }
+
+        Builder builder = null;
+        if (httpMethod.equals(HttpMethod.POST)) {
+            builder = new Request.Builder().url(url).post(formBody.build());
+        } else if (httpMethod.equals(HttpMethod.PUT)) {
+            builder = new Request.Builder().url(url).put(formBody.build());
+        } else if (httpMethod.equals(HttpMethod.DELETE)) {
+            builder = new Request.Builder().url(url).delete(formBody.build());
+        }
+
+        if (null != headers && 0 != headers.size()) {
+            for (Entry<String, String> entry : headers.entrySet()) {
+                builder.addHeader(entry.getKey(), entry.getValue());
+            }
+        }
+
+        Request request = builder.build();
+
+        Response response = null;
+        try {
+            response = okHttpClient.newCall(request).execute();
+            return response;
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 发送请求 (包含文件表单)
+     *
+     * @author
+     * @param httpMethod
+     * @param url
+     * @param headers
+     * @param params
+     * @param formFilePartList
+     * @return
+     */
+    public static Response call(HttpMethod httpMethod, String url, Map<String, String> headers,
+            Map<String, String> params, List<FormFilePart> formFilePartList) {
+
+        okhttp3.MultipartBody.Builder multipartBodyBuilder = new MultipartBody.Builder()
+                .setType(MultipartBody.ALTERNATIVE);
+
+        if (null != params) {
+            for (Entry<String, String> entry : params.entrySet()) {
+                multipartBodyBuilder.addFormDataPart(entry.getKey(), entry.getValue());
+            }
+        }
+
+        if (CollectionUtils.isNotEmpty(formFilePartList)) {
+            MediaType type = MediaType.parse("application/octet-stream");
+            for (FormFilePart part : formFilePartList) {
+                RequestBody fileBody = RequestBody.create(type, part.getFile());
+                multipartBodyBuilder.addFormDataPart(part.getParamName(), part.getFilename(), fileBody);
+            }
+        }
+
+        Builder builder = new Request.Builder().url(url).post(multipartBodyBuilder.build());
+        if (null != headers && 0 != headers.size()) {
+            for (Entry<String, String> entry : headers.entrySet()) {
+                builder.addHeader(entry.getKey(), entry.getValue());
+            }
+        }
+
+        Request request = builder.build();
+
+        Response response = null;
+        try {
+            response = okHttpClient.newCall(request).execute();
+            return response;
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+}

+ 64 - 0
src/main/java/cn/com/qmth/archive/utils/ResouceUtil.java

@@ -0,0 +1,64 @@
+package cn.com.qmth.archive.utils;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+
+import org.apache.commons.io.IOUtils;
+
+public class ResouceUtil {
+	public static URL getUrl(String path) {
+		try {
+			ClassLoader classLoader = ResouceUtil.class.getClassLoader();
+
+			URL url = classLoader.getResource(path);
+			return url;
+		} catch (Exception e) {
+			throw new RuntimeException(e);
+		}
+	}
+	public static InputStream getStream(String path) {
+		try {
+			ClassLoader classLoader = ResouceUtil.class.getClassLoader();
+
+			URL url = classLoader.getResource(path);
+			return url.openStream();
+		} catch (Exception e) {
+			throw new RuntimeException(e);
+		}
+	}
+	public static String getContent(String path) {
+		try {
+			ClassLoader classLoader = ResouceUtil.class.getClassLoader();
+
+			URL url = classLoader.getResource(path);
+			return readFileContent(url.openStream());
+		} catch (Exception e) {
+			throw new RuntimeException(e);
+		}
+	}
+	
+	@SuppressWarnings("deprecation")
+	private static String readFileContent(InputStream in) {
+		StringBuilder content = new StringBuilder();
+		InputStreamReader streamReader = null;
+		BufferedReader bufferedReader = null;
+		try {
+			String encoding = "UTF-8";
+			streamReader = new InputStreamReader(in, encoding);
+			bufferedReader = new BufferedReader(streamReader);
+			String line;
+			while ((line = bufferedReader.readLine()) != null) {
+				content.append(line);
+			}
+		} catch (Exception e) {
+			throw new RuntimeException(e);
+		} finally {
+			IOUtils.closeQuietly(in);
+			IOUtils.closeQuietly(streamReader);
+			IOUtils.closeQuietly(bufferedReader);
+		}
+		return content.toString();
+	}
+}

+ 63 - 0
src/main/java/cn/com/qmth/archive/utils/SSLSocketClient.java

@@ -0,0 +1,63 @@
+package cn.com.qmth.archive.utils;
+
+import javax.net.ssl.*;
+import java.security.KeyStore;
+import java.security.SecureRandom;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+
+public class SSLSocketClient {
+
+    // 获取这个SSLSocketFactory
+    public static SSLSocketFactory getSSLSocketFactory() {
+        try {
+            SSLContext sslContext = SSLContext.getInstance("SSL");
+            sslContext.init(null, getTrustManager(), new SecureRandom());
+            return sslContext.getSocketFactory();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    // 获取TrustManager
+    private static TrustManager[] getTrustManager() {
+        return new TrustManager[] { new X509TrustManager() {
+
+            @Override
+            public void checkClientTrusted(X509Certificate[] chain, String authType) {
+            }
+
+            @Override
+            public void checkServerTrusted(X509Certificate[] chain, String authType) {
+            }
+
+            @Override
+            public X509Certificate[] getAcceptedIssuers() {
+                return new X509Certificate[] {};
+            }
+        } };
+    }
+
+    // 获取HostnameVerifier
+    public static HostnameVerifier getHostnameVerifier() {
+        return (s, sslSession) -> true;
+    }
+
+    public static X509TrustManager getX509TrustManager() {
+        X509TrustManager trustManager = null;
+        try {
+            TrustManagerFactory trustManagerFactory = TrustManagerFactory
+                    .getInstance(TrustManagerFactory.getDefaultAlgorithm());
+            trustManagerFactory.init((KeyStore) null);
+            TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
+            if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
+                throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers));
+            }
+            trustManager = (X509TrustManager) trustManagers[0];
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        return trustManager;
+    }
+}

+ 74 - 0
src/main/java/cn/com/qmth/archive/utils/SpringContextHolder.java

@@ -0,0 +1,74 @@
+package cn.com.qmth.archive.utils;
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.core.ResolvableType;
+import org.springframework.stereotype.Component;
+
+/**
+ * spring context holder.
+ *
+ */
+@Component
+public class SpringContextHolder implements ApplicationContextAware {
+
+	private static ApplicationContext ctx = null;
+
+	@Override
+	public void setApplicationContext(ApplicationContext ctx) {
+		SpringContextHolder.ctx = ctx;
+	}
+
+	public static ApplicationContext getApplicationContext() {
+		return ctx;
+	}
+
+	public static Object getBean(String name) {
+		return ctx.getBean(name);
+	}
+
+	public static <T> T getBean(String name, Class<T> requiredType) {
+		return ctx.getBean(name, requiredType);
+	}
+
+	public static <T> T getBean(Class<T> requiredType) {
+		return ctx.getBean(requiredType);
+	}
+
+	public static Object getBean(String name, Object... args) {
+		return ctx.getBean(name, args);
+	}
+
+	public static <T> T getBean(Class<T> requiredType, Object... args) {
+		return ctx.getBean(requiredType, args);
+	}
+
+	public static boolean containsBean(String name) {
+		return ctx.containsBean(name);
+	}
+
+	public static boolean isSingleton(String name) {
+		return ctx.isSingleton(name);
+	}
+
+	public static boolean isPrototype(String name) {
+		return ctx.isPrototype(name);
+	}
+
+	public static boolean isTypeMatch(String name, ResolvableType typeToMatch) {
+		return ctx.isTypeMatch(name, typeToMatch);
+	}
+
+	public static boolean isTypeMatch(String name, Class<?> typeToMatch) {
+		return ctx.isTypeMatch(name, typeToMatch);
+	}
+
+	public static Class<?> getType(String name) {
+		return ctx.getType(name);
+	}
+
+	public static String[] getAliases(String name) {
+		return ctx.getAliases(name);
+	}
+
+}

+ 39 - 0
src/main/resources/application.properties

@@ -0,0 +1,39 @@
+#
+# ********** server config **********
+#
+server.port=8091
+server.servlet.session.timeout=PT2H
+server.servlet.context-path=/
+spring.servlet.multipart.max-request-size=100MB
+spring.servlet.multipart.max-file-size=100MB
+com.qmth.mybatis.log-level=debug
+com.qmth.mybatis.block-attack=false
+#
+# ********** db config **********
+#
+db.host=192.168.10.83
+db.port=3306
+db.database=exam_cloud_dev
+com.qmth.datasource.username=exam_cloud_dev
+com.qmth.datasource.password=exam_cloud_dev_123
+com.qmth.datasource.url=jdbc:mysql://${db.host}:${db.port}/${db.database}?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2b8&rewriteBatchedStatements=true
+com.qmth.datasource.max-pool-size=200
+com.qmth.datasource.min-idle=10
+#
+# ********** sys config **********
+#
+com.qmth.logging.root-level=info
+com.qmth.logging.file-path=./log/ai-marking.log
+
+
+spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
+spring.jackson.time-zone=GMT+8
+
+com.qmth.solar.app-version=@project.version@
+
+##################################setting##########################################
+
+#1,2,3.....
+archive.workids=38,69
+
+##################################setting##########################################

+ 16 - 0
src/main/resources/mapper/MarkingMapper.xml

@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="cn.com.qmth.archive.dao.MarkingDao">
+	<select id="getWork"
+		resultType="map">
+		SELECT <foreach collection="cols" item="val" separator=",">
+				w.${val}
+			</foreach>
+		from ec_m_mark_work w
+		where w.id in (
+			<foreach collection="workids" item="val" separator=",">
+				#{val}
+			</foreach>
+			)
+	</select>
+</mapper>