java文件压缩

java文件压缩(zip)

public class FileZipUtil {
    /**
     * 压缩 temp 包含 temp
     * @param zipFileName D:/1.zip   这里是压缩后的文件名称全路径
     * @param targetFile  D:/zip/temp  这里是将要压缩的文件路径
     */
    public static void zip(String zipFileName, File targetFile) {
        try (ZipOutputStream zop = new ZipOutputStream(new FileOutputStream(zipFileName));) {
            diGuiZip(zop, targetFile, targetFile.getName());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * @param out        ZipOutputStream
     * @param targetFile 目标文件
     * @param name       目标文件的所在位置的路径名 例如:  temp   temp/Arrays.java  temp/asList.java
     * @param bos        BufferedOutputStream
     */
    private static void diGuiZip(ZipOutputStream out, File targetFile, String name) throws IOException {
        if (!targetFile.exists()) {
            return; //不存在此文件,直接返回
        }
        //如果 是个目录
        if (targetFile.isDirectory()) {
            File[] files = targetFile.listFiles();
            //空目录
            if (files.length == 0) {
                out.putNextEntry(new ZipEntry(name + "/"));
            }
            for (File file : files) {
                diGuiZip(out, file, name + "/" + file.getName());
            }
        } else {
            out.putNextEntry(new ZipEntry(name));
            try (InputStream in = new FileInputStream(targetFile);
                 BufferedInputStream bis = new BufferedInputStream(in)) {
                byte[] bytes = new byte[1024];
                int len = -1;
                while ((len = bis.read(bytes)) != -1) {
                    out.write(bytes, 0, len);
                }
            }
        }
    }
}
使用Channel

为什么要用 Channel呢?因为在NIO中新出了 Channel和 ByteBuffer。正是因为它们的结构更加符合操作系统执行I/O的方式,所以其速度相比较于传统IO而言速度有了显著的提高。Channel就像一个包含着煤矿的矿藏,而 ByteBuffer则是派送到矿藏的卡车。也就是说我们与数据的交互都是与 ByteBuffer的交互。
在NIO中能够产生 FileChannel的有三个类。分别是 FileInputStream、 FileOutputStream、以及既能读又能写的 RandomAccessFile。

    public static void zip(String zipFileName, File targetFile) {
        try (ZipOutputStream zop = new ZipOutputStream(new FileOutputStream(zipFileName));
             WritableByteChannel writableByteChannel = Channels.newChannel(zop);) {
            diGuiZip(zop, targetFile, targetFile.getName(), writableByteChannel);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void diGuiZip(ZipOutputStream out, File targetFile,
                                 String name, WritableByteChannel channelW) throws IOException {
        if (!targetFile.exists()) {
            return; //不存在此文件,直接返回
        }
        //如果 是个目录
        if (targetFile.isDirectory()) {
            File[] files = targetFile.listFiles();
            //空目录
            if (files.length == 0) {
                out.putNextEntry(new ZipEntry(name + "/"));
            }
            for (File file : files) {
                diGuiZip(out, file, name + "/" + file.getName(), channelW);
            }
        } else {
            try (FileChannel fileChannel = new FileInputStream(targetFile).getChannel();) {
                out.putNextEntry(new ZipEntry(name));
                fileChannel.transferTo(0, targetFile.length(), channelW);
            }
        }
    }

This method is potentially much more efficient than a simple loop that reads from this channel and writes to the target channel. Many operating systems can transfer bytes directly from the filesystem cache to the target channel without actually copying them.
上面就是对transferTo的解释,使用transferTo使两个通道直接相连,比循环一个Channel读取出来然后再循环写入另一个Channel好。操作系统能够直接传输字节从文件系统缓存到目标的Channel,而不需要实际的copy阶段。
copy阶段就是从内核空间转到用户空间的一个过程
线程处于内核空间称属于内核态,线程处于用户空间属于用户态。
应用程序都是属于用户空间,要想访问核心资源(内核空间有权限访问),就需要调用内核中暴漏的接口,称之为系统调用。
非直接缓冲区:在虚拟机内存中创建,易回收,但占用虚拟机内存开销,处理中有复制过程。

直接缓冲区:在虚拟机内存外,开辟的内存,IO操作直接进行,不再对其进行复制,但创建和销毁开销大。

缺点:1、不安全,消耗更多,因为它不是在JVM中直接开辟空间。这部分内存的回收只能依赖于系统的垃圾回收机制,垃圾什么时候回收不受我们控制。
2、数据写入物理内存缓冲区中,程序就丧失了对这些数据的管理,即什么时候这些数据被最终写入从磁盘只能由操作系统来决定,应用程序无法再干涉。

使用内存映射文件

NIO中新出的另一个特性就是内存映射文件,其原理也是在内存中开辟了一段直接缓冲区。与数据直接作交互。

    public static void zip(String zipFileName, File targetFile) {
        try (ZipOutputStream zop = new ZipOutputStream(new FileOutputStream(zipFileName));
             WritableByteChannel writableByteChannel = Channels.newChannel(zop);) {
            diGuiZip(zop, targetFile, targetFile.getName(), writableByteChannel);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void diGuiZip(ZipOutputStream out, File targetFile,
                                 String name, WritableByteChannel channelW) throws IOException {
        if (!targetFile.exists()) {
            return; //不存在此文件,直接返回
        }
        //如果 是个目录
        if (targetFile.isDirectory()) {
            File[] files = targetFile.listFiles();
            //空目录
            if (files.length == 0) {
                out.putNextEntry(new ZipEntry(name + "/"));
            }
            for (File file : files) {
                diGuiZip(out, file, name + "/" + file.getName(), channelW);
            }
        } else {
            out.putNextEntry(new ZipEntry(name));
            MappedByteBuffer mappedByteBuffer = new RandomAccessFile(targetFile.getAbsolutePath(), "r")
                    .getChannel()
                    .map(FileChannel.MapMode.READ_ONLY, 0, targetFile.length());
            channelW.write(mappedByteBuffer);
        }
    }
使用pipe

Java NIO 管道是2个线程之间的单向数据连接。Pipe有一个source通道和一个sink通道。其中source通道用于读取数据,sink通道用于写入数据。
写入线程会阻塞至有读线程从通道中读取数据。如果没有数据可读,读线程也会阻塞至写线程写入数据。

    public static void zip(String zipFileName, File targetFile) {
        try (WritableByteChannel writableByteChannel = Channels.newChannel(new FileOutputStream(zipFileName));) {
            Pipe pipe = Pipe.open();
            //异步任务
            CompletableFuture.runAsync(() -> runTask(pipe, targetFile));
            //获取 读通道
            ReadableByteChannel readableByteChannel  = pipe.source();
            ByteBuffer buffer = ByteBuffer.allocate(1024 * 10);
            while (readableByteChannel.read(buffer) >= 0) {
                buffer.flip();
                writableByteChannel.write(buffer);
                buffer.clear();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void runTask(Pipe pipe, File targetFile) {
        try (ZipOutputStream zos = new ZipOutputStream(Channels.newOutputStream(pipe.sink()));
             WritableByteChannel writeChannel = Channels.newChannel(zos)) {
            diGuiZip(zos, targetFile, targetFile.getName(), writeChannel);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void diGuiZip(ZipOutputStream out, File targetFile,
                                 String name, WritableByteChannel writeChannel) throws IOException {
        if (!targetFile.exists()) {
            return; //不存在此文件,直接返回
        }
        //如果 是个目录
        if (targetFile.isDirectory()) {
            File[] files = targetFile.listFiles();
            //空目录
            if (files.length == 0) {
                out.putNextEntry(new ZipEntry(name + "/"));
            }
            for (File file : files) {
                diGuiZip(out, file, name + "/" + file.getName(), writeChannel);
            }
        } else {
            out.putNextEntry(new ZipEntry(name));
            FileChannel fileChannel = new FileInputStream(targetFile).getChannel();
            fileChannel.transferTo(0, targetFile.length(), writeChannel);
            fileChannel.close();
        }
    }
加密压缩
<dependency>
	  <groupId>net.lingala.zip4j</groupId>
	  <artifactId>zip4j</artifactId>
	  <version>2.6.1</version>
</dependency>
ZipOutputStream z = ZipOutputStream(fos, password);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值