【Java】压缩流和解压缩流

在开发中它们都是有自己的应用场景的。

例如我们现在要传输的数据比较大,这个时候就可以先压缩,再传输。

image-20240505083916867

既然有压缩,那肯定会有解压。当接收到一个压缩包后,压缩包肯定是不能直接使用的。

我们需要先进行解压,得到里面的每一个文件,这样才能用里面的数据。

image-20240505084024096

因此我们要学习的就是压缩和解压。

来看下它们在 IO流体系 中的位置。

解压缩流 主要就是读取压缩包里面的文件,所以它是读,属于 输入流

而右边的 压缩流,是将文件中的数据写到压缩包中,所以它是写,属于 输出流

image-20240505084205261

我们先来学习相对比较简单的 解压缩流


解压缩流

如果想要解压,在电脑中首先要有一个压缩包,这个压缩包需要是 zip 作为后缀的,因为Java中只能识别这个格式的。

压缩包里面的每一个文件 在Java中都是一个 ZipEntry对象

因此解压的本质:把每一个 ZipEntry 按照层级拷贝到本地另一个文件夹中。

解压缩流的名字叫做 ZipInputStreamZip 是它的作用,表示跟压缩包是有关系的;InputStream 表示它是用来读取的。

public static void main(String[] args) throws IOException {
    //1.创建一个File表示要解压的压缩包
    File src = new File("D:\\aaa.zip");
    //2.创建一个File表示解压的目的地
    File dest = new File("D:\\");
    //调用方法
    unzip(src, dest);
}

//定义一个方法用来解压
public static void unzip(File src, File dest) throws IOException {
    ZipInputStream zip = new ZipInputStream(new FileInputStream(src));
    ZipEntry entry = zip.getNextEntry();
    System.out.println(entry);
}

如果多调用几次 getNextEntry() 会怎么样

ZipInputStream zip = new ZipInputStream(new FileInputStream(src));
ZipEntry entry1 = zip.getNextEntry();
System.out.println(entry1);

ZipEntry entry2 = zip.getNextEntry();
System.out.println(entry2);

ZipEntry entry3 = zip.getNextEntry();
System.out.println(entry3);

运行结果如下,aaa 有了,还有 aaa 里面的 aa.txt,以及 bbbb文件夹

image-20240505090722122

第一次打印出的 ZipEntryaaa/

image-20240505090343857

这个 aaa 并不是 aaa.zip 本身,而是它里面的文件夹。

image-20240505090212167

那么思考:它能一直获取吗?可以将子文件中的东西也获取到吗?

如果获取不到,它会返回什么呢?null 还是 -1

没关系,我们都可以来试一下,因此这些知识点我们都可以通过不断的尝试进行总结

我也不知道这个文件夹中一共有多少 ZipEntry对象,但我知道绝对没有100个,因此这里直接循环100次来看看。

for (int i = 0; i < 100; i++) {
    ZipEntry entry1 = zip.getNextEntry();
    System.out.println(entry1);
}

可以发现,当我们将压缩包里面的所有文件和文件夹都获取完毕了,这个时候再强行获取,就会返回 null,因此循环的结束条件我们已经知道了。

并且,子文件夹中的文件和文件夹也都可以获取到。

image-20240505091542965

因此有了这个特性后,就不需要自己递归了,直接写一个循环就行了。

getNextEntry() 的底层,它会把压缩包里的每一个文件,或者文件夹全都获取到。

public static void main(String[] args) throws IOException {
    //1.创建一个File表示要解压的压缩包
    File src = new File("D:\\aaa.zip");
    //2.创建一个File表示解压的目的地
    File dest = new File("D:\\");

    //调用方法
    unzip(src,dest);
}

//定义一个方法用来解压
public static void unzip(File src,File dest) throws IOException {
    //解压的本质:把压缩包里面的每一个文件或者文件夹读取出来,按照层级拷贝到目的地当中
    //创建一个解压缩流用来读取压缩包中的数据,解压缩流中关联基本流,再去关联压缩包的路径
    ZipInputStream zip = new ZipInputStream(new FileInputStream(src));
    //之前我们说过,`压缩包里面的每一个文件` 在Java中都是一个 `ZipEntry对象`。
    //因此在这我们需要先获取到压缩包里面的每一个zipentry对象,调用getNextExtry()
    //表示当前在压缩包中获取到的文件或者文件夹
    ZipEntry entry;
    while((entry = zip.getNextEntry()) != null){
        System.out.println(entry);
        if(entry.isDirectory()){ // entry没有isFile(),但是有isDirectory(),用来判断这个是不是一个文件夹
            //文件夹:需要在目的地dest处创建一个同样的文件夹
            File file = new File(dest,entry.toString());
            file.mkdirs();
        } else {
            //文件:需要读取到压缩包中的文件,并把他存放到目的地dest文件夹中(按照层级目录进行存放)
            //entry.toString():将它变成字符串,即文件夹名
            FileOutputStream fos = new FileOutputStream(new File(dest, entry.toString()));
            int b;
            //读取的时候是使用zip进行读取
            while((b = zip.read()) != -1){
                //写到目的地
                fos.write(b);
            }
            fos.close();
            //表示在压缩包中的一个文件处理完毕了,需要调用closeEntry()
            zip.closeEntry();
        }
    }

    zip.close();
}

压缩流(压缩单个文件)

压缩:将多个文件 / 文件夹变成一个压缩包。

在压缩的时候需要注意:压缩包里面的每一个文件或文件夹都是一个 ZipEntry对象

压缩本质:把每一个(文件/文件夹)看成 ZipEntry对象 放到压缩包中。

需求1:把D:\\a.txt打包成一个压缩包
public static void main(String[] args) throws IOException {
    //1.创建File对象表示要压缩的文件
    File src = new File("D:\\a.txt");
    //2.创建File对象表示压缩包的位置
    File dest = new File("D:\\");
    //3.调用方法用来压缩
    toZip(src, dest);
}

/*
 *   作用:压缩
 *   参数一:表示要压缩的文件
 *   参数二:表示压缩包的位置
 * */
public static void toZip(File src, File dest) throws IOException {
    //1.创建压缩流关联压缩包,这个压缩包名字一定要手动加上,压缩流中关联基本流,再去关联压缩包的路径
    ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(new File(dest, "a.zip")));
    //2.创建ZipEntry对象,表示压缩包里面的每一个文件和文件夹
    //参数:压缩包里面的路径
    ZipEntry entry = new ZipEntry("a.txt");
    //3.把ZipEntry对象放到压缩包当中
    zos.putNextEntry(entry);
    //创建ZipOutputStream的时候肯定会在D盘下新建a.zip的压缩包,但是压缩包里还是空的,所以下面创建了ZipEntry对象,这个ZipEntry就表示压缩包里的a.txt,但是这个文件中还没有数据!我们还需要将原始文件中的数据再写到ZipEntry中才行。
    //4.把src文件中的数据写到压缩包当中
    FileInputStream fis = new FileInputStream(src); // 从src中读取数据
    int b; // 表示读到的数据
    while ((b = fis.read()) != -1) {
        // 将读取到的数据写到压缩包中
        zos.write(b);
    }
    //closeEntry()表示当前的文件已经书写完毕
    zos.closeEntry();
    zos.close();
}

压缩流(压缩文件夹)

由于上一个代码,我们只需要压缩一个文件,因此写 a.txt 是没有问题的,但是如果我们写 aaa\\a.txt 会出现什么情况呢?

ZipEntry entry = new ZipEntry("aaa\\a.txt");

可以发现它是先有了一个 aaa文件夹,然后在 aaa文件夹 里面才有了 a.txt

image-20240505095442624

我们再来加一层 bbb

ZipEntry entry = new ZipEntry("aaa\\bbb\\a.txt");
image-20240505095606566

由此可知,ZipEntry 里面的参数其实也表示压缩包里面的路径,通过这个特点,我们就可以去压缩包中创建不同层级的子文件夹了。

有的同学写路径的时候会像下面这种写法一样

//1.创建File对象表示要压缩的文件夹
File src = new File("D:\\aaa");
//2.创建File对象表示压缩包放在哪里(压缩包的父级路径)
File destParent = new File("D:\\aaa.zip");

但是如果真的这么写,代码会非常的不方便。

例如以后我不想压缩 aaa文件夹 了,而是想压缩 bbb文件夹,那么下面的压缩包名字还需要跟着改,太麻烦了。

我的需求是:如果我上面改了,下面也能跟着一起变动。

public static void main(String[] args) throws IOException {
    //1.创建File对象表示要压缩的文件夹
    File src = new File("D:\\aaa");
    //2.创建File对象表示压缩包放在哪里(压缩包的父级路径)
    //getParentFile():获取父级路径
    File destParent = src.getParentFile();//D:\\
    //3.创建File对象表示压缩包的路径
    File dest = new File(destParent, src.getName() + ".zip");
    //4.创建压缩流关联压缩包,解压缩流中关联基本流,再去关键压缩包的路径
    ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(dest));
    //5.获取src里面的每一个文件,变成ZipEntry对象,放入到压缩包当中
    //由于src是一个文件夹,如果你想要获取到里面的每一个文件,需要递归
    toZip(src, zos, src.getName());//aaa
    //6.释放资源
    zos.close();
}

/*
 *   作用:获取src里面的每一个文件,变成ZipEntry对象,放入到压缩包当中
 *   参数一:数据源
 *   参数二:压缩流,这个压缩流已经关联好了压缩包
 *   参数三:压缩包内部的路径
 * */
public static void toZip(File src, ZipOutputStream zos, String name) throws IOException {
    //1.进入src文件夹
    File[] files = src.listFiles();
    //2.遍历数组
    //file依次表示src里面的文件或者文件夹
    for (File file : files) {
        if (file.isFile()) {
            //3.判断-文件,变成ZipEntry对象,放入到压缩包当中
            ZipEntry entry = new ZipEntry(name + "\\" + file.getName());//aaa\\no1\\a.txt
            zos.putNextEntry(entry);
            //读取文件中的数据,写到压缩包
            FileInputStream fis = new FileInputStream(file);
            int b;
            while ((b = fis.read()) != -1) {
                zos.write(b);
            }
            fis.close();
            zos.closeEntry();
        } else {
            //4.判断-文件夹,递归
            toZip(file, zos, name + "\\" + file.getName());
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值