java zip 压缩工具类




java zip 压缩工具类


目录结构










环境依赖

<!--这里作者使用的是 springboot 2.3.2.RELEASE 版本-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <version>2.3.2.RELEASE</version>
</dependency>
<!--请去 maven 中央仓库 检查与其他依赖冲突的情况再决定版本 !!!-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.75</version>
    <scope>compile</scope>
</dependency>










工具类

import com.alibaba.fastjson.JSONObject;
import org.springframework.util.StringUtils;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

/**
 * 压缩文件工具。
 * 不生成中间过渡文件,直接操作压缩包。
 * 仅支持一层文件【非文件夹,非压缩包】操作。
 * 
 * 注意:
 * 不适用于【高并发场景】。
 *
 * @author zhangsan coder
 * @since 2022-09-4 14:52
 **/
public class ZipUtil {


    /**
     * 压缩 zip 到 outputStream 位置。
     * 压缩 zip 文件。
     * <p>
     * 注意:inputStreamList 与 zipFileNames 长度必须一一对应,文件数据与名称一对一的对应关系。
     * <p>
     * 如:<p>
     * inputStreamList[0] -> zipFileNames[0]。<p>
     * inputStreamList[1] -> zipFileNames[1]。<p>
     * inputStreamList[2] -> zipFileNames[2]。<p>
     *
     * @param inputStreamList 被压缩的列表数据。【String形式】
     * @param zipFileNames    zip 中的每个文件的名称。【长度必须严格与 inputStreamList 同长度】
     * @param outputStream    压缩包输出位置。
     */
    public static void toZip(List<InputStream> inputStreamList, List<String> zipFileNames, OutputStream outputStream) throws IOException {
        if (inputStreamList.size() <= 0) {
            return;
        }
        if (zipFileNames.size() <= 0) {
            return;
        }
        // 设置压缩流:直接写入response,实现边压缩边下载
        ZipOutputStream zipos = null;
        try {
            zipos = new ZipOutputStream(new BufferedOutputStream(outputStream));
            zipos.setMethod(ZipOutputStream.DEFLATED); //设置压缩方法
        } catch (Exception e) {
            return;
        }
        // 循环将文件写入压缩流
        DataOutputStream os = null;

        for (int i = 0; i < inputStreamList.size(); i++) {
            zipos.putNextEntry(new ZipEntry(StringUtils.isEmpty(zipFileNames.get(i)) ? "解压的数据" + i : zipFileNames.get(i)));
            os = new DataOutputStream(zipos);
            byte[] b = new byte[4 * 1024];
            int length = 0;
            while ((length = inputStreamList.get(i).read(b)) != -1) {
                os.write(b, 0, length);
            }
            inputStreamList.get(i).close();
            zipos.closeEntry();
        }

        // 关闭流
        try {
            if (os != null) {
                os.flush();
                os.close();
            }
            zipos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 解压 zip 并 获取解压后的数据。【单个 zip】
     * 注意:
     * 得确保 zip 文件中的数据为 JSON 数据;
     * 且 JSON 数据必须与 toClass 属性字段保持一致!
     * 属性取值范围只能在 toClass 属性名内,超过范围则报错!
     * 仅支持一层解压。
     * <p>
     * 如:
     * 有字段:a、b、c、d。
     * 转换成功:a、b。    a、b、c。      a、c。        a、b、c、d。
     * 转换失败:a、e。    a、b、f。
     *
     * @param zipInputStream 压缩流。
     * @param toClass        转换的类型。
     * @return 解压后的文件数据。
     */
//    @SneakyThrows
    public static <T> List<T> unZipForJsonToList(ZipInputStream zipInputStream, Class<T> toClass) throws IOException, NullPointerException {
        if (toClass == null) {
            throw new NullPointerException("提取的对象不能为空!");
        }
        if (zipInputStream == null) {
            throw new NullPointerException("压缩流不能为空!");
        }

        List<T> list = new ArrayList<>();
        Map<String, String> zipFile = getZipFile(zipInputStream);

        //map 的entry遍历方法
        for (Map.Entry<String, String> en : zipFile.entrySet()) {

//            System.out.println(en.getKey() + " ==》 " + en.getValue());
            T t = JSONObject.parseObject(en.getValue(), toClass);
            list.add(t);
        }
        if (list.size() <= 0) {
            throw new NullPointerException("zip 提取数据失败!");
        }
        return list;
    }

    /**
     * 解压 zip 并 获取解压后的数据。【单个 zip】
     * 注意:
     * 得确保 zip 文件中的数据为 JSON 数据;
     * 且 JSON 数据必须与 toClass 属性字段保持一致!
     * 属性取值范围只能在 toClass 属性名内,超过范围则报错!
     * 仅支持一层解压。
     * <p>
     * 如:
     * 有字段:a、b、c、d。
     * 转换成功:a、b。    a、b、c。      a、c。        a、b、c、d。
     * 转换失败:a、e。    a、b、f。
     *
     * @param zipInputStream 压缩流。
     * @param toClass        转换的类型。
     * @return 解压后的文件数据。
     */
//    @SneakyThrows
    public static <T> Map<String, T> unZipForJsonToMap(ZipInputStream zipInputStream, Class<T> toClass) throws IOException, NullPointerException {
        if (toClass == null) {
            throw new NullPointerException("提取的对象不能为空!");
        }
        if (zipInputStream == null) {
            throw new NullPointerException("压缩流不能为空!");
        }

        Map<String, String> zipFile = getZipFile(zipInputStream);
        Map<String, T> returnMap = new ConcurrentHashMap<>();

        //map 的entry遍历方法
        for (Map.Entry<String, String> en : zipFile.entrySet()) {
//            System.out.println(en.getKey() + " ==》 " + en.getValue());
            T t = JSONObject.parseObject(en.getValue(), toClass);
            returnMap.put(en.getKey(), t);
        }
        if (returnMap.size() <= 0) {
            throw new NullPointerException("zip 提取数据失败!");
        }
        return returnMap;
    }

    /**
     * 根据入参,返回提取到的文件字符串形式数据。
     *
     * @param srcZip 源zip。
     * @return 返回提取到的文件数据内容【字符串形式】,若没有获取到内容,则返回 null 。
     * @throws IOException
     */
    public static Map<String, String> getZipFile(ZipInputStream srcZip) throws IOException {

        //读取一个目录
        ZipEntry nextEntry = null;
        // 压缩包内的文件数据【字符串形式】。
        Map<String, String> stringMap = new ConcurrentHashMap<>();
        StringBuilder jsonString = new StringBuilder();
        //不为空,则进入循环。
        while ((nextEntry = srcZip.getNextEntry()) != null) {
            String name = nextEntry.getName();
//            System.out.println("name = " + name);

//            for (String fileName : fileNames) {
//                if (!StringUtils.isEmpty(name) && name.equalsIgnoreCase(fileName)) {
//                    System.out.println("压缩包中存在当前文件名:" + fileName);
//                    System.out.println();

            // 提取文件数据。
            try (
                    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                    BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(byteArrayOutputStream)
            ) {


                int n;
                byte[] bytes = new byte[4 * 1024];
//            byte[] bytes = new byte[1];
                while ((n = srcZip.read(bytes)) != -1) {
                    bufferedOutputStream.write(bytes, 0, n);
                    // 获取文件数据。
                    jsonString.append(new String(bytes, 0, n));
                    //                System.out.print(jsonString);
                }
                stringMap.put(name, jsonString.toString());
                jsonString = new StringBuilder();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                //关闭当前文件。
                srcZip.closeEntry();
            }


//            System.out.println();
//            System.out.println("================================================================================");
//            System.out.println();
        }
        return stringMap;
    }


    //    public static void main(String[] args) throws IOException {
    private static void main(String[] args) throws IOException {

//        List<Route> routes = unZipForJson(
//                new ZipInputStream(new FileInputStream(new File("C:\\\\Users\\\\zhangsan\\\\Desktop\\\\测速.zip")))
//                , Route.class
//        );
//        System.out.println("============================ 转换的对象列表 ==========================");
//        routes.forEach(System.out::println);
//        System.out.println("===================================================================");
    }
}










遇到的问题



异常:org.apache.catalina.connector.ClientAbortException: java.io.IOException: Broken pipe。

原因:传输流其中一端突然断开关闭导致的。

  • 针对于 File ,就是文件传输过程中,物理断开连接。
  • 针对于 socket 断网,网络超时导致的请求断开等等。

注: 在Java中,没有具体的BrokenPipeException。 将此类错误包含在另一个异常,例如java.io.IOException:Broken pipe


问题分析:

  1. 报文过大:处理的报文过大,导致客户的端无法解析报文。
  2. 文件过大:处理时间过长,由于执行时间较长或频率较高,程序或服务器出发超时直接结束进程。
  3. 重复提交:处理时间过长导致当客户端重复发送请求,而上次请求尚未完成,下次请求会close上次请求。
  4. 数据库配置问题:mysql配置文件忘记配置wait_timeout参数,导致数据库连接顺序关闭(RS、PS、CONN)。
  5. 另外就是:Java虚拟机内存太小或者使用低版本的JVM,出发垃圾回收。

参考文献
地址:参考文件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值