zip4j实现多线程压缩


使用的jar包:zip4j_1.3.2.jar
基本功能:
针对ZIP压缩文件创建、添加、分卷、更新和移除文件
(读写有密码保护的Zip文件)
(支持AES 128/256算法加密)
(支持标准Zip算法加密)
(支持zip64格式)
(支持Store(仅打包,默认不压缩,不过可以手动设置大小)和Deflate压缩方法
(针对分块zip文件创建和抽出文件)
(支持编码)
(进度监控)
压缩方式(3种):
static final int COMP_STORE = 0;(仅打包,不压缩) (对应好压的存储)
static final int COMP_DEFLATE = 8;(默认) (对应好压的标准)
static final int COMP_AES_ENC = 99;

压缩级别有5种:(默认0不压缩)级别跟好压软件是对应的;
static final int DEFLATE_LEVEL_FASTEST = 1;
static final int DEFLATE_LEVEL_FAST = 3;
static final int DEFLATE_LEVEL_NORMAL = 5;
static final int DEFLATE_LEVEL_MAXIMUM = 7;
static final int DEFLATE_LEVEL_ULTRA = 9;
加密方式:
static final int ENC_NO_ENCRYPTION = -1;(默认,没有加密方法,如果采用此字段,会报错”没有提供加密算法”)
static final int ENC_METHOD_STANDARD = 0;
static final int ENC_METHOD_AES = 99;
AES Key Strength:
(默认-1,也就是ENC_NO_ENCRYPTION)
static final int AES_STRENGTH_128 = 0x01;
static final int AES_STRENGTH_192 = 0x02;
static final int AES_STRENGTH_256 = 0x03;

从构造方法可以默认情况:
compressionMethod = Zip4jConstants.COMP_DEFLATE;
encryptFiles = false;//不设密码
readHiddenFiles = true;//可见
encryptionMethod = Zip4jConstants.ENC_NO_ENCRYPTION;//加密方式不加密
aesKeyStrength = -1;//
includeRootFolder = true;//
timeZone = TimeZone.getDefault();//

**
 * 情景教学压缩操作类
 */
public class CompressHandler extends Thread {

    private Logger logger = Logger.getLogger(CompressHandler.class);

    /**
     * 存储目录名和对应目录下的文件地址
     */
    private Map<String, List<ResourceInfo>> resourceMap;

    /**
     * 压缩类中的配置文件
     */
    private String configJson;

    /**
     * 配置文件map key为压缩包中的文件名,value为文件内容
     */
    Map<String, String> configJsonMap;

    /**
     * 回调接口
     */
    private ZipOperate zipOperate;

    /**
     * 加密标志
     */
    private boolean encrypt = false;

    /**
     * 线程名称
     */
    private String threadName = Thread.currentThread().getName();

    /**
     * 取消标志
     */
    private boolean cancelFlag;

    public CompressHandler() {
    }

    public CompressHandler(Map<String, List<ResourceInfo>> resourceMap, Map<String, String> configJsonMap, ZipOperate zipOperate) {
        this(resourceMap, configJsonMap, zipOperate, false);
    }

    public CompressHandler(Map<String, List<ResourceInfo>> resourceMap, Map<String, String> configJsonMap, ZipOperate zipOperate, boolean encrypt) {
        this.resourceMap = resourceMap;
        this.configJsonMap = configJsonMap;
        this.zipOperate = zipOperate;
        this.encrypt = encrypt;
    }

    public CompressHandler(Map<String, List<ResourceInfo>> resourceMap, String configJson, ZipOperate zipOperate) {
        this(resourceMap, configJson, zipOperate, false);
    }

    public CompressHandler(Map<String, List<ResourceInfo>> resourceMap, String configJson, ZipOperate zipOperate, boolean encrypt) {
        this.resourceMap = resourceMap;
        this.configJson = configJson;
        this.zipOperate = zipOperate;
        this.encrypt = encrypt;
    }

    @Override
    public void run() {
        if (zipOperate != null) {
            zipOperate.beforeCompress();
        }
        String fileName = UUID.randomUUID().toString().replace("-", "").concat(".zip");
        String tempDir = SuperdiamondConfig.getConfig(SuperdiamondConfig.SYSTEM_TMMP_FILE_PATH);
        File tempDirFile = new File(tempDir);
        if (!tempDirFile.exists()) {
            tempDirFile.mkdir();
        }
        String filePath = tempDir.concat("/").concat(fileName);
        //存储生成本地压缩包的文件
        List<File> encryptFileList = new ArrayList<>();
        try {
            ZipFile zipFile = new ZipFile(filePath);
            logger.info("线程" + threadName + "  开始压缩,压缩的文件名为:" + fileName);
            long startTime = System.currentTimeMillis();
            logger.info("线程" + threadName + "  开始时间为:" + startTime);
            ZipParameters parameters = new ZipParameters();
            //设置压缩方式和压缩级别
            parameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE);
            parameters.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_NORMAL);
            //当开启压缩包密码加密时
            boolean dirEncrypt = false;
            if (encrypt || Boolean.parseBoolean(SuperdiamondConfig.getConfig(SuperdiamondConfig.ENABLE_ENCRYPT_ZIP))) {
                logger.info("线程" + threadName + "  该压缩包需要加密");
                addEncryptParameters(parameters);
                dirEncrypt = true;
            }
            for (String dir : resourceMap.keySet()) {
                logger.info("线程" + threadName + "  添加目录:" + dir);
                List<ResourceInfo> resourceInfoList = resourceMap.get(dir);
                for (ResourceInfo resourceInfo : resourceInfoList) {
                    String resourceFileName = resourceInfo.getFileName();
                    logger.info("线程" + threadName + "  添加文件:" + resourceFileName);
                    /**
                     * 20180921判断目录名是否为空字符串 true不创建目录 by lizhang10
                     */
                    if(StringUtils.isNotEmpty(dir)){
                        parameters.setFileNameInZip(dir + "/" + resourceFileName);
                    }else{
                        parameters.setFileNameInZip(resourceFileName);
                    }
                    parameters.setSourceExternalStream(true);
                    InputStream inputStream = resourceInfo.getInputStream();
                    if (inputStream == null) {
                        //获取文件流
                        String fileUrl = resourceInfo.getFileUrl();
                        long startTime1 = System.currentTimeMillis();
                        logger.info("线程" + threadName + "  开始获取文件流,地址:" + fileUrl + "  开始时间:" + startTime1);
                        inputStream = getInputStream(fileUrl);
                        long endTime1 = System.currentTimeMillis();
                        logger.info("线程" + threadName + "  结束获取文件流,地址:" + fileUrl + "  结束时间:" + endTime1 + "  耗时毫秒: " + (endTime1 - startTime1));
                    }
                    //当压缩包没有加密时,则从文件属性中取是否进行加密
                    if(!dirEncrypt && resourceInfo.isEncrypt()){
                        //如果是zip的话,则对文件进行解压,然后再加密
                        if(resourceFileName.endsWith(".zip")){
                            long saveStartTime = System.currentTimeMillis();
                            //网络文件路径
                            String sourceFileName = tempDir.concat("/").concat(UUID.randomUUID().toString().concat(".zip"));
                            logger.info("线程" + threadName + " 该文件["+resourceFileName+"]是zip且需要加密,开始存到本地,路径为:"+sourceFileName+"开始时间:" + saveStartTime);
                            //加密文件路径
                            String encryptFileName = tempDir.concat("/").concat(UUID.randomUUID().toString().concat(".zip"));
                            File file = new File(sourceFileName);
                            FileOutputStream outputStream = new FileOutputStream(file);
                            byte[] buffer = new byte[8*1024];
                            int len = 0;
                            while ((len = inputStream.read(buffer)) != -1){
                                outputStream.write(buffer,0,len);
                            }
                            outputStream.close();
                            inputStream.close();
                            long saveEndTime = System.currentTimeMillis();
                            logger.info("线程" + threadName + " 该文件是zip,存到本地完成,结束时间:" + saveEndTime + "  耗时:"+(saveEndTime - saveStartTime));
                            long extractStartTime = System.currentTimeMillis();
                            logger.info("线程" + threadName + " ,开始进行解压加密,开始时间为:" + extractStartTime);
                            ZipFile sourceZipFile = new ZipFile(file);
                            String extractDir = tempDir.concat("/").concat(UUID.randomUUID().toString());
                            sourceZipFile.extractAll(extractDir);
                            long extractEndTime = System.currentTimeMillis();
                            ZipFile encryptZipFile = new ZipFile(encryptFileName);
                            ZipParameters parameters2 = new ZipParameters();
                            //设置压缩方式和压缩级别
                            parameters2.setCompressionMethod(Zip4jConstants.COMP_DEFLATE);
                            parameters2.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_NORMAL);
                            addEncryptParameters(parameters2);
                            //添加获取文件的文件和目录
                            File[] fileArray = new File(extractDir).listFiles();
                            for (File file1 : fileArray) {
                                if(file1.isDirectory()){
                                    encryptZipFile.addFolder(file1,parameters2);
                                } else {
                                    encryptZipFile.addFile(file1,parameters2);
                                }
                            }
                            if(!StringUtils.isEmpty(resourceInfo.getConfigJson())){
                                parameters2.setFileNameInZip("config.json");
                                parameters2.setSourceExternalStream(true);
                                encryptZipFile.addStream(new ByteArrayInputStream(resourceInfo.getConfigJson().getBytes("utf8")),parameters2);
                            }
                            logger.info("线程" + threadName + " ,完成解压加密,结束时间为:" + extractEndTime + " 耗时: " + (extractEndTime-extractStartTime));
                            //删除文件
                            sourceZipFile.getFile().delete();
                            //删除目录下的文件
                            deleteFolder(new File(extractDir));
                            File encryptFile = encryptZipFile.getFile();
                            inputStream = new FileInputStream(encryptFile);
                            encryptFileList.add(encryptFile);
                        }
                    }
                    //获取需要打包的文件流
                    zipFile.addStream(inputStream, parameters);
                    inputStream.close();
                }
            }
            parameters.setSourceExternalStream(true);
            //如果configJsonMap不为空,且length不为0,则遍历加入到压缩包中
            if (configJsonMap != null && configJsonMap.size() > 0) {
                for (String resourceName : configJsonMap.keySet()) {
                    logger.info("线程" + threadName + " 添加配置文件" + resourceName);
                    parameters.setFileNameInZip(resourceName);
                    String value = configJsonMap.get(resourceName);
                    zipFile.addStream(new ByteArrayInputStream(value.getBytes("utf8")), parameters);
                }
            }
            if (!StringUtils.isEmpty(configJson)) {
                logger.info("线程" + threadName + "  添加文件config.json");
                parameters.setFileNameInZip("config.json");
                zipFile.addStream(new ByteArrayInputStream(configJson.getBytes("utf8")), parameters);
            }
            logger.info("线程" + threadName + "  压缩文件" + fileName + "完成");
            long endTime = System.currentTimeMillis();
            logger.info("线程" + threadName + "  结束时间为:" + endTime + "  耗时毫秒:" + (endTime - startTime));
            long startTime2 = System.currentTimeMillis();
            logger.info("线程" + threadName + "  开始将压缩包上传到文件服务.......开始时间:" + startTime2);
            FileInfo fileInfo = new CystrageUtil().uploadToRemoteServer(fileName, filePath, null);
            long endTime2 = System.currentTimeMillis();
            logger.info("线程" + threadName + "  上传完成.......结束时间:" + endTime2 + "耗时毫秒:  " + (endTime2 - startTime2));
            if (cancelFlag){
                throw new InterruptedException("线程" + threadName + "  取消压缩");
            }
            //如果传入了后续操作接口,则将文件服务返回的类传入
            if (zipOperate != null) {
                zipOperate.afterCompress(fileInfo, zipFile);
                zipFile.getFile().delete();
            }
        } catch (Exception e) {
            logger.error("线程" + threadName + "  文件压缩出错...........文件内容:" + configJson, e);
            if (zipOperate != null) {
                zipOperate.errorCompress();
            }
            //删除对应压缩包
            new File(filePath).delete();
        } finally {
            try {
                //关流
                inputStream.close();
            } catch (IOException e) {
                inputStream = null;
            }
            //删除本地生成的压缩文件
            //存储生成本地压缩包的文件
            for (File file : encryptFileList) {
                file.delete();
            }
        }
    }

    /**
     * 删除文件夹下的所有文件
     * @param sourceDir
     */
    private void deleteFolder(File sourceDir) {
        if(sourceDir.isDirectory()){
            File[] files = sourceDir.listFiles();
            for (File file : files) {
                deleteFolder(file);
            }
        } else {
            sourceDir.delete();
        }
        sourceDir.delete();
    }

    private void addEncryptParameters(ZipParameters parameters) {
        String password = SuperdiamondConfig.getConfig(SuperdiamondConfig.ZIP_COMPRESS_PASSWORD);
        parameters.setEncryptFiles(true);
        parameters.setEncryptionMethod(Zip4jConstants.ENC_METHOD_AES);
        parameters.setAesKeyStrength(Zip4jConstants.AES_STRENGTH_256);
        parameters.setPassword(password.toCharArray());
    }


    /**
     * 获取输入流
     *
     * @param fileUrl
     * @return
     */
    InputStream inputStream;

    private InputStream getInputStream(String fileUrl) throws Exception {
        if(cancelFlag){
            throw new InterruptedException("线程" + threadName + "  取消压缩");
        }
        int retry = 1;
        while (retry <= 3) {
            try {
                int index = fileUrl.lastIndexOf("/");
                String prefix = fileUrl.substring(0, index + 1);
                String fileName = fileUrl.substring(index + 1);
                URL url = new URL(prefix + URLEncoder.encode(fileName, "utf8"));
                URLConnection connection = url.openConnection();
                connection.setDoInput(true);
                inputStream = connection.getInputStream();
                return inputStream;
            } catch (Exception e) {
                if (retry == 1) {
                    logger.error("线程" + threadName + "  获取文件出错,文件地址:" + fileUrl, e);
                }
                logger.error("开始重试第" + retry + "次");
                //由于测试环境服务器承载能力比较差,当获取失败后,睡眠一段时间再重试
                if(Boolean.parseBoolean(SuperdiamondConfig.getConfig(SuperdiamondConfig.SYSTEM_TEST_ENVIRONMENT))){
                    Thread.currentThread().sleep(Long.parseLong(SuperdiamondConfig.getConfig(SuperdiamondConfig.SYSTEM_SLEEP_TIME)));
                }
                if (retry == 3) {
                    throw new Exception(e);
                }
                retry++;
            }
        }
        return null;
    }

    public void setCancelFlag(Boolean cancelFlag){
        this.cancelFlag = cancelFlag;
    }

    @Override
    public void interrupt() {
        if (inputStream != null) {
            try {
                inputStream.close();
            } catch (IOException e) {
                inputStream = null;
                logger.info("线程" + threadName + "  关闭io流失败");
            }
        }
        super.interrupt();
    }

    public static class ResourceInfo {
        /**
         * 文件名称
         */
        private String fileName;

        /**
         * 文件地址
         */
        private String fileUrl;

        /**
         * 输入流
         */
        private InputStream inputStream;

        /**
         * 是否要为当前文件添加config,json
         */
        private String configJson;

        /**
         * 改文件是否加密
         */
        private boolean encrypt;

        public InputStream getInputStream() {
            return inputStream;
        }

        public void setInputStream(InputStream inputStream) {
            this.inputStream = inputStream;
        }

        public String getFileName() {
            return fileName;
        }

        public void setFileName(String fileName) {
            this.fileName = fileName;
        }

        public String getFileUrl() {
            return fileUrl;
        }

        public void setFileUrl(String fileUrl) {
            this.fileUrl = fileUrl;
        }

        public boolean isEncrypt() {
            return encrypt;
        }

        public void setEncrypt(boolean encrypt) {
            this.encrypt = encrypt;
        }

        public String getConfigJson() {
            return configJson;
        }

        public void setConfigJson(String configJson) {
            this.configJson = configJson;
        }
    }
}

文章作者: 知己而知彼
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 知己而知彼 !
评论
  目录