什么是压缩文件
Zip文件是一种常见的归档文件格式,通常用于将多个文件打包成一个文件进行传输或存储。Zip文件中可以包含多个文件和文件夹,这些文件和文件夹会被压缩成一个单独的文件,以减少存储空间和传输带宽的消耗。 人话:使文件更小,更集中,更利于数据传递
Zip文件格式是一种开放的标准,它由PKWARE公司于1989年发布,目前已经被广泛支持和使用。Zip文件格式不仅可以在Windows和Mac OS X等操作系统上使用,还可以在Linux和其他UNIX操作系统上使用,因此它是一种跨平台的归档格式。人话:跨平台,各个操作系统均能接收并处理
Zip文件通常可以通过多种方式进行创建和解压缩,例如使用Windows操作系统附带的压缩工具、使用第三方的压缩软件(如WinRAR、7-Zip等)、使用命令行工具(如Linux系统上的zip和unzip命令)等。在Java程序中,可以使用Java自带的ZipFile和ZipOutputStream类来读取和创建Zip文件。 人话:可以使用操作系统自带的压缩软件,也可以使用Java的具体zip类来操作zip
本文主要是讲解Java里面对zip文件的操作:
ZipInputStream与InputStream的关系
InputStream是所有输入流的父类,包括字节流与字符流,它是按字节进行读取数据的,然后我们的压缩文件是以文件为单位进行解压缩的。 因此可以使用FileInputStream来将文件读入,再使用ZipInputStream(InputStream的装饰器类,不是直接继承InputStream)来对其进行解压缩
具体讲讲ZipInputStream与InputStream的关系吧:
首先,ZipInputStream 继承了 InflaterInputStream。InflaterInputStream类中定义了一些方法,如fill()、readBits()等。ZipInputStream类重写了这些方法,并添加了一些Zip文件特有的逻辑,例如从Zip文件中读取文件头、解压缩文件数据等。ZipInputStream的重写方法read也调用了父类的read方法:super.read(), 满足装饰器模式,调用自身方法添加特殊处理的同时,父类的方法正常调用
InflaterInputStream 继承了 FilterInputStream,并添加了对压缩数据的解压缩功能,同时满足装饰器模式。
FilterInputStream 继承了 InputStream ,将InputStream作为属性,为其装饰(添加功能)。
这样一层层的为InputStream的实现类添加功能,ZipInputStream就是其中之一的装饰器。
开始使用ZipInputStream
// 新建文件输入实体类new FileInputStream();
// 使用ZipInputStream装饰,添加独特的处理压缩文件的方法
ZipInputStream zip = new ZipInputStream(new FileInputStream("C:/Users/Lenovo/Desktop/笔记.zip"),
Charset.forName("GBK"))
编码格式很重要,压缩文件的编码格式默认为UTF-8,但是也不全是,为此,大家可能常常出错误:
上面就是编码格式出错的情况。最简单的做法:出错了你就修改编码格式,GBK,UTF-8,GB2312等编码格式来回换。
也可以利用第三方工具或库可以对Zip文件名进行解码,比如[Apache Commons Compress](程序员的福音 - Apache Commons Compress - 知乎 (zhihu.com))
完整的解压文件并且读取文件内容的代码如下
完整解压代码,可以拿去直接用
import java.io.*;
import java.nio.charset.Charset;
import java.util.zip.*;
public class Main {
// 获得file下的文件内容
public static void getFileContent(File file){
if(file.isDirectory()){
File[] files = file.listFiles();
for (File file1 : files) {
getFileContent(file1);
}
}
else {
byte[] buffer = new byte[1024];
try (BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(file), buffer.length)) {
int bytesRead;
while ((bytesRead = inputStream.read(buffer, 0, buffer.length)) != -1) {
for (int i = 0; i < bytesRead; i++) {
// 我以字节流输出的,格式太多了统一为字节流,可以在这里判断文件类型
System.out.print(buffer[i] + " ");
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws IOException {
String zipFilePath = "C:\Users\Lenovo\Desktop\biji.zip"; // Zip文件路径
String unzipFolderPath = "C:\Users\Lenovo\Desktop\biji"; // 解压后的文件夹路径
// 创建解压后的文件夹
File unzipFolder = new File(unzipFolderPath);
if (!unzipFolder.exists()) {
unzipFolder.mkdir();
}
// 创建Zip文件输入流
FileInputStream fis = new FileInputStream(zipFilePath);
ZipInputStream zis = new ZipInputStream(new BufferedInputStream(fis), Charset.forName("GBK"));
// 遍历Zip文件中的每个文件,并解压到指定文件夹中
ZipEntry entry;
byte[] buffer = new byte[1024];
while ((entry = zis.getNextEntry()) != null) {
String fileName = entry.getName();
File file = new File(unzipFolderPath + File.separator + fileName);
if (entry.isDirectory()) {
file.mkdirs();
} else {
FileOutputStream fos = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(fos, buffer.length);
int count;
// 读取的内容放入缓冲区字节数组
while ((count = zis.read(buffer, 0, buffer.length)) != -1) {
// 将缓冲区的内容写入文件
bos.write(buffer, 0, count);
}
// 这时剩余的部分在缓冲区,需要将没占满缓冲区的部分也一并写入文件,调用flush()
bos.flush();
bos.close();
}
}
// 写成try(Resource)就可以不用手动关闭
// 关闭输入流
zis.close();
fis.close();
// 读取解压后的文件
getFileContent(unzipFolder);
}
}
小伙伴们可能会疑惑flush()的用法
BufferedOutputStream wirite源码:
@Override
public synchronized void write(byte b[], int off, int len) throws IOException {
if (len >= buf.length) {
/* If the request length exceeds the size of the output buffer,
flush the output buffer and then write the data directly.
In this way buffered streams will cascade harmlessly. */
flushBuffer();
out.write(b, off, len);
return;
}
if (len > buf.length - count) {
flushBuffer();
}
System.arraycopy(b, off, buf, count, len);
count += len;
}
方法首先判断要写入的字节数是否大于缓冲区的大小,如果是,则先将缓冲区中的数据写入到输出流中,以避免数据丢失或者混乱。如果要写入的字节数小于等于缓冲区的大小,方法会检查缓冲区中剩余的空间是否足够存储要写入的数据,如果不够,则先将缓冲区中的数据写入到输出流中,以释放空间。如果剩余的空间足够存储要写入的数据,则将数据写入到缓冲区中,并更新缓冲区的计数器。注意这最后一句话,写入到缓冲区,并没有写入到输出流中,因此手动调用flush()方法
小贴士: _MACOSX文件夹是在Mac OS X操作系统上创建的隐藏文件夹。它通常出现在你通过Mac电脑将文件或文件夹压缩成ZIP文件时。这个文件夹包含与压缩文件相关的元数据,例如存储在文件中的资源信息和属性。 在大多数情况下,_MACOSX文件夹对于文件的使用没有任何影响。但是,如果您将ZIP文件发送给使用其他操作系统(如Windows)的人员,可能会导致问题,因为Windows系统无法处理这些元数据文件。在这种情况下,_MACOSX文件夹可能会显示在压缩文件中,并且可能会导致问题或不必要的混乱。 因此,如果您要与其他操作系统的用户共享ZIP文件,最好将_MACOSX文件夹从ZIP文件中删除。