• Java处理ZIP文件的解决方案——Zip4J(不解压直接通过InputStream形式读取其中的文件,解决中文乱码)


    一、JDK内置操作Zip文件
    其实,在JDK中已经存在操作ZIP的工具类:ZipInputStream。

    基本使用:

    public static Map<String, String> readZipFile(String file) throws Exception {
    Map<String, String> resultMap = new HashMap<String, String>();
    Charset gbk = Charset.forName("GBK");
    ZipFile zf = new ZipFile(file, gbk); // 此处可以用无Charset的构造函数,但是即使是设置为GBK也是处理不了中文的,后面会再说
    InputStream in = new BufferedInputStream(new FileInputStream(file));
    ZipInputStream zin = new ZipInputStream(in);
    ZipEntry ze;

    while ((ze = zin.getNextEntry()) != null) {
    if (ze.isDirectory()) {
    } else {
    long size = ze.getSize(); // 文件的大小
    String name = ze.getName(); // 获取文件名称
    // 具体对其中每个文件的操作和获取信息,可以参考JDK API
    if (size > 0) {
    InputStream inputStream = zf.getInputStream(ze); // 拿到文件流
    // …… 业务逻辑
    }
    }
    }
    zin.closeEntry();

    return resultMap;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    更多的操作可以参考其他人的总结,本文的重点在于描述JDK自带zip操作API的不便之处,从而引出Zip4J。

    JDK自带ZIP API有个非常重大的问题:不支持ZIP中目录中的中文。如下异常:

    java.lang.IllegalArgumentException: MALFORMED
    at java.util.zip.ZipCoder.toString(Unknown Source)
    at java.util.zip.ZipInputStream.readLOC(Unknown Source)
    at java.util.zip.ZipInputStream.getNextEntry(Unknown Source)
    at com.mskj.test.md5.BigFileMD5.readZipFile(BigFileMD5.java:131)
    at com.mskj.test.md5.BigFileMD5.checkFileMD5(BigFileMD5.java:42)
    at com.mskj.test.TestMain.main(TestMain.java:12)
    1
    2
    3
    4
    5
    6
    7
    我的ZIP中目录:“TestTest-01Te你好……”,当然导致该异常的原因不只是中文的问题,但是大部分情况下都是。对于JDK自带的ZIP API还有一些其他问题,比如操作解压和压缩API接口不方便(带有密码ZIP)等。当然,网上有许多人说是已经可以解决中文的问题,但是比较麻烦,并不是每次都能够成功,我本地也有尝试过。

    在我们的项目中,通过不断的比较,最终还是选择了ZIP4J。

    二、ZIP4J
    官网:http://www.lingala.net/zip4j/

    zip4j默认采用的是UTF-8编码,所以本身支持中文(但是,个人建议还是在读取zip文件后,立即设置字符集),同时也支持密码,而且支持多种压缩算法,可以说功能强大,但使用起来却非常简单,当然,如果有其他需求,需要自己从官网上看API。如果你百度或者谷歌ZIP4J的用法,会有许多好的博客和教程,再此不再赘述。
    例如:
    https://blog.csdn.net/ditto_zhou/article/details/82977395
    https://www.cnblogs.com/622698abc/p/3389855.html
    https://rensanning.iteye.com/blog/1836727
    ……

    我想结合一下我项目中需求以及网上许多同仁的问题(不解压zip文件,直接通过InputStream的形式读取其中的文件信息),说一个简单的应用:不解压ZIP文件的前提下,直接利用流(InuptStream)形式读取其中的文件,并读取文件的MD5值。

    类似于JDK自带ZipInputStream的形式读取zip文件,由于ZIP4J的ZipInputStream不具备ZipInputStream.getNextEntry()),所以,在ZIP4J中只能通过FileHeader来进行循环。而且,JDK自带API中获取ZIP其中的文件流InputStream时,需要:

    ZipFile zf = new ZipFile(file);
    InputStream inputStream = zf.getInputStream(ZipEntry);
    1
    2
    所以,对应ZIP4J就只能ZipInputStream(该类时InputStream的子类)。

    具体代码如下:

    import java.util.List;
    import net.lingala.zip4j.core.ZipFile;
    import net.lingala.zip4j.io.ZipInputStream;
    import net.lingala.zip4j.model.FileHeader;

    public class ZIP4JUtils {
    /**
    * @param file
    * @throws Exception
    */
    public static void readZipFileMD5ByZip4J(String file) throws Exception {
    file.replaceAll("\\", "/");
    ZipFile zFile = new ZipFile(file);
    // 此处最好立即设置字符集
    zFile.setFileNameCharset("GBK");
    if (!zFile.isValidZipFile()) {
    return ;
    }

    // 获取ZIP中所有文件的FileHeader,以便后面对zip中文件进行遍历
    List<FileHeader> list = zFile.getFileHeaders();
    // 此时list的size包括:文件夹、子文件夹、文件的个数
    System.out.println(list.size());
    // 遍历其中的文件
    for (FileHeader fileHeader : list) {
    String fileName = fileHeader.getFileName();
    // fileName会将目录单独读出来,而且带有路径分割符
    if (fileName.endsWith("/") || fileName.endsWith("\\") || fileName.endsWith("\")) {
    System.out.println(fileName + " 这是一个文件夹。");
    continue;
    }else {
    ZipInputStream inputStream = zFile.getInputStream(fileHeader);
    String Md5String = BigFileMD5.getStreamMD5(inputStream);
    System.out.println(fileName + " 这是一个文件,该文件的MD5值:" + Md5String);
    }
    }
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    其中计算MD5值的类如下:

    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.io.InputStream;
    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    import java.util.ArrayList;
    import java.util.List;
    import org.apache.commons.codec.binary.Hex;

    public class BigFileMD5 {
    static MessageDigest MD5 = null;
    static List<File> list = new ArrayList<File>();
    static{
    try{
    MD5 = MessageDigest.getInstance("MD5");
    }catch(NoSuchAlgorithmException e){
    e.printStackTrace();
    }
    }

    /**
    * 对一个文件获取md5值
    * @return md5串
    */
    public static String getStreamMD5(InputStream fileInputStream) {
    try {
    byte[] buffer = new byte[8192];
    int length;
    while ((length = fileInputStream.read(buffer)) != -1) {
    MD5.update(buffer, 0, length);
    }

    return new String(Hex.encodeHex(MD5.digest()));
    } catch (FileNotFoundException e) {
    e.printStackTrace();
    return null;
    } catch (IOException e) {
    e.printStackTrace();
    return null;
    } finally {
    try {
    if (fileInputStream != null)
    fileInputStream.close();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }

    /**
    * 对一个文件获取md5值
    * @return md5串
    */
    public static String getMD5(File file) {
    FileInputStream fileInputStream = null;
    try {
    fileInputStream = new FileInputStream(file);
    byte[] buffer = new byte[8192];
    int length;
    while ((length = fileInputStream.read(buffer)) != -1) {
    MD5.update(buffer, 0, length);
    }

    return new String(Hex.encodeHex(MD5.digest()));
    } catch (FileNotFoundException e) {
    e.printStackTrace();
    return null;
    } catch (IOException e) {
    e.printStackTrace();
    return null;
    } finally {
    try {
    if (fileInputStream != null)
    fileInputStream.close();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    输出结果:

    Test-01/ 这是一个文件夹。
    Test-01/BigFielMD5.jar 这是一个文件,该文件的MD5值:db55e6677f7087754f11eaf84b2d728e
    Test-01/Te/ 这是一个文件夹。
    Test-01/Te/Test.txt 这是一个文件,该文件的MD5值:c6354a0eb36fac331c138eec7a4826ef
    Test-01/Te/你好/ 这是一个文件夹。
    Test-01/Te/你好/Hello/ 这是一个文件夹。
    Test-01/Te/你好/Hello/antlr-2.7.2.jar 这是一个文件,该文件的MD5值:a73459120df5cadf75eaa98453433a01
    Test-01/Te/你好/Hello/antlr-2.7.2.jar.sha1 这是一个文件,该文件的MD5值:3aa5bc052867b339a0b6ed0546f9b336
    Test-01/Te/你好/Hello/antlr-2.7.2.pom 这是一个文件,该文件的MD5值:b1136da0c12ce8ffc18d00f8742256ee
    Test-01/Te/你好/Hello/antlr-2.7.2.pom.sha1 这是一个文件,该文件的MD5值:ebd38323fc24c8aab361b2a7660ec666
    Test-01/测试.txt 这是一个文件,该文件的MD5值:0b60aa5d9aa241ca6063495086e38e95
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    这样就实现了,Java不解压直接读取zip文件和文件内容(InputStream的形式)。

    如有不对之处,还请指教!
    --------------------- 

  • 相关阅读:
    全国疫情可视化地图(第三阶段)
    全国疫情可视化地图(第二阶段)
    JAVAWEB调用数据库显示图形化显示
    全国疫情可视化地图(第一阶段)
    第二周学习总结
    个人介绍及目前现状
    循环列表下练习循环子数组
    构建之法读书笔记(三)
    构建之法读书笔记(二)
    变叶木
  • 原文地址:https://www.cnblogs.com/hyhy904/p/10947575.html
Copyright © 2020-2023  润新知