vlambda博客
学习文章列表

每日一例 | java实现文件压缩和解压

java作为一门相对比较完善的语言,有着特别丰富的接口和系统类,今天我们来看两个特殊的I/O流,这两个类可以协助我们实现文件的压缩和解压:GZIPOutputStream/GZIPInputStreamZipOutputStream/ZipInputStream,这两对压缩流都位于java.util.zip包下。这两个类,在web开发时也经常会用到,比如文件的批量上传和批量下载,所以掌握这两对流,可以有助于你很多web需求的实现。今天我们就来看下如何用他们来实现文件的压缩和解压。

GZ压缩

先上代码:

/**
     * 压缩gz
     * @param filePath
     * @return
     * @throws Exception
     */

    private static String compressFileByGZ(String filePath) throws Exception {
        File file = new File(filePath);
        System.out.println(file.getName());
        String outputFIleName = file.getName() + ".gz";
        GZIPOutputStream gzipOutputStream = new GZIPOutputStream(new FileOutputStream(outputFIleName));

        FileInputStream fileInputStream = new FileInputStream(filePath);
        byte[] bytes = new byte[1024];
        int read;
        while ((read = fileInputStream.read(bytes)) != -1) {
            gzipOutputStream.write(bytes);
        }
        fileInputStream.close();
        gzipOutputStream.close();

        return outputFIleName;
    }

GZ这里演示的只是单个文件的,多文件压缩从GZIP的接口来看,应该是不支持的,至少原生不支持,因为压缩包里面的文件名和GZ压缩包文件名是一致的,所以多文件压缩应该是有问题的,后面我再研究下,今天鉴于时间,就不深入了。压缩后的效果:

每日一例 | java实现文件压缩和解压

这里选了一张图片,压缩率43%,原文件大小为17kb,压缩后只有9.68kb

GZ解压

解压就是把压缩包里面的文件拿出来,相比于zipgz解压后的文件名只能取压缩文件的名字,因为它本身没有保留压缩的文件名。

/**
     * 解压GZ
     * @param filePath
     * @throws Exception
     */

    public static void extractGz(String filePath) throws Exception {
        FileInputStream fileInputStream = new FileInputStream(filePath);
        GZIPInputStream gzipInputStream = new GZIPInputStream(fileInputStream);
        FileOutputStream fileOutputStream = new FileOutputStream(new File(filePath).getName().replace(".gz"""));
        byte[] bytes = new byte[1024];
        int x ;
        while ((x = gzipInputStream.read(bytes)) != -1) {
            fileOutputStream.write(bytes);
        }
        fileInputStream.close();
        gzipInputStream.close();
        fileOutputStream.close();
    }

解压后的文件:

每日一例 | java实现文件压缩和解压

ZIP压缩

zip的压缩就比较有意思了,原生支持多文件压缩,需要注意的是每个文件要单独创建一个ZipEntry,它就相当于压缩包里面每一个单独的文件,它包含被压缩文件的文件信息,所以解压的时候,可以拿到之前压缩的文件名。

/**
     * 压缩zip
     *
     * @param filePath
     * @return
     * @throws Exception
     */

    private static String compressFileByZIP(String filePath) throws Exception {
        File file = new File(filePath);
        System.out.println(file.getName());
        String outputFIleName = file.getName() + ".zip";
        ArrayList<File> fileList = new ArrayList<>();
        if (file.isDirectory()) {
            fileList.addAll(Arrays.asList(file.listFiles()));
        } else {
            fileList.add(file);
        }
        FileInputStream fileInputStream = null;
        CheckedOutputStream checkedOutputStream = new CheckedOutputStream(new FileOutputStream(outputFIleName), new Adler32());
        ZipOutputStream zipOutputStream = new ZipOutputStream(checkedOutputStream);
        for (File f : fileList) {
            if (f.isDirectory()) {
                continue;
            }
            zipOutputStream.putNextEntry(new ZipEntry(f.getName()));
            fileInputStream = new FileInputStream(f);
            byte[] bytes = new byte[1024];
            int read;
            while ((read = fileInputStream.read(bytes)) != -1) {
                zipOutputStream.write(bytes);
            }
        }
        byte[] bytes = new byte[1024];
        int read;
        while ((read = fileInputStream.read(bytes)) != -1) {
            zipOutputStream.write(bytes);
        }
        fileInputStream.close();
        zipOutputStream.close();

        return outputFIleName;
    }

zip本身就支持多文件,所以这里就演示了多文件压缩,单个文件也做了兼容。压缩文件效果:

被压缩文件夹,共212个文件

每日一例 | java实现文件压缩和解压

文件大小

每日一例 | java实现文件压缩和解压

压缩文件

压缩后34.8MB,和我电脑上安装的压缩软件做了对比,压缩后大小是一样的

每日一例 | java实现文件压缩和解压

ZIP解压

解压我们前面说了,它的ZipEntry包含被压缩文件的信息,所以就很简单了:

 /**
     * 解压zip文件
     * @param filePath
     * @return
     * @throws Exception
     */

    public static String extractZip(String filePath) throws Exception{
        FileInputStream fileInputStream = new FileInputStream(filePath);
        CheckedInputStream checkedInputStream = new CheckedInputStream(fileInputStream, new Adler32());
        ZipInputStream zipInputStream = new ZipInputStream(checkedInputStream);
        ZipEntry zipEntry;

        FileOutputStream fileOutputStream = null;
        File savePath = new File(filePath.replace(".zip"""));
        if (!savePath.exists()) {
            savePath.mkdir();
        }
        BufferedInputStream bufferedInputStream = new BufferedInputStream(zipInputStream);
        while ((zipEntry = zipInputStream.getNextEntry()) != null) {
            fileOutputStream = new FileOutputStream(savePath.getName() + "/" + zipEntry.getName());
            int x;
            byte[] bytes = new byte[1024];
            while ((x = bufferedInputStream.read(bytes)) != -1) {
                fileOutputStream.write(bytes);
            }
            fileOutputStream.close();
        }
        zipInputStream.close();
        fileInputStream.close();
        return null;
    }

第一个循环是从流里面拿出压缩包中的每一个文件(zipEntry),第二个循环就是从流中把被压缩文件还原成压缩前的格式。

ZIP补充

这里要补充两点,一个是CheckedInputStream/CheckedOutputStream。这一对流是为各种流提供校验和的,也就是校验输出输入数据的;另一个也和CheckedOutputStream/CheckedInputStream有关,就是构建这两个流的时候传的Adler32在,这里其实表示的是计算和校验文件的校验和的方式(Checksum),取值有两种,分别是Adler32CRC32,但是因为Adler32更快,所以这里我们用了它,CRC32虽然慢,但也更准确。

总结

总的来说,java文件压缩和解压方面的内容还是比较简单的,主要涉及了I/O流方面的知识点,各位小伙伴只要掌握了上面两种压缩和解压的方式,大部分web端开发的压缩和解压需求也可以迎刃而解了,当然最重要的一点是,要自己动手做,不然就真的变成了:

眼睛:我会了……每日一例 | java实现文件压缩和解压

手:那你来每日一例 | java实现文件压缩和解压

眼睛:……

大家早上好呀,阳光明媚的一天又开始了……

项目路径:

https://github.com/Syske/example-everyday

本项目会每日更新,让我们一起学习,一起进步,遇见更好的自己,加油呀

- END -