解压缩流
Java中只能识别格式为zip的压缩包。
其实压缩包中的每一个文件或者文件夹都是一个ZipEntry对象,解压其实就是把每一个ZipEntry对象读取出来并放到对应的目录下。
我知道了,要是压缩的文件里有中文,记得创建ZipInputStream的时候设置解码规则为GBK。
下面是ZipEntry对象的转换为字符串后的样子,经过观察可以发现,在遍历时一定是先遍历文件夹,然后再是文件夹下的内容,所以不会出现某个文件的父级目录不存在的情况。
下面是解压某个压缩包的代码:
关于解压缩流中如何读取某个文件中数据?只需要调用流中的read方法就可以了。
public class ZIO1 {
public static void main(String[] args) throws IOException {
File src = new File("D:\\Java学习资料\\day29-IO(其他流)\\资料.zip");
File dest = new File("bbb");
unzip(src, dest);
}
public static void unzip(File src, File dest) throws IOException {
// 首先获取解压缩流
ZipInputStream zis = new ZipInputStream(new FileInputStream(src), Charset.forName("GBK"));
ZipEntry zipEntry;
// 依次读取解压缩包下的每一个文件或者文件夹
while ((zipEntry = zis.getNextEntry()) != null) {
// 文件夹
if (zipEntry.isDirectory()) {
File file = new File(dest, zipEntry.toString());
file.mkdirs();
} else { // 文件
FileOutputStream fos = new FileOutputStream(new File(dest, zipEntry.toString()));
int b;
while ((b = zis.read()) != -1) {
fos.write(b);
}
fos.close();
zis.closeEntry();
}
zis.close();
}
}
}
压缩流
压缩流属于输出流。
关于压缩流主要就是创建ZipEntry对象时new ZipEntry(path2);的参数,搞不明白了也就知道怎么回事了。
1、压缩单个文件
- 首先创建压缩包的路径;
- 然后把文件变成ZipEntry对象;
- 再调用putZipEntry()方法放到压缩包里面,这一步其实只是创建了一个空的文件或者文件夹;
- 接着创建输入流读取文件中的内容;
- 最后利用压缩流写入文件或者文件夹的内容。
代码实现:
public class ZIO2 {
public static void main(String[] args) throws IOException {
File src = new File("bbb//123.txt");
File dest = new File("bbb");
zip(src, dest);
}
private static void zip(File src, File dest) throws IOException {
String path1 = dest + "/" + src.getName().split("\\.")[0] + ".zip";
// 压缩流的路径
ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(new File(path1)));
String path2 = src.getName().split("\\.")[0] + "/" + src.getName();
// 转换为ZipEntry对象
ZipEntry zip = new ZipEntry(path2);
zos.putNextEntry(zip);
// 读取的文件路径
FileInputStream fis = new FileInputStream(src);
// 要写入的文件路径,不需要再创建,直接写入压缩流就可以
int b;
while ((b = fis.read()) != -1) {
zos.write(b);
}
fis.close();
zos.closeEntry();
zos.close();
}
}
2、压缩文件夹
假设拿到a.txt就创建a.txt的ZipEntry对象放到压缩包中,再读a.txt的数据写进去啊,这个过程结束了,循环才结束去读下一个b.txt啊。
问题1:刚开始访问的是当前模块下的com/liu/PIO包,但是使用listFiles方法获取这个包下的所有内容时值总是为null,截图如下,也不知道为什么,后面干脆就换了其他的包进行压缩。
问题2:在 Java 中,推荐使用 File.separator 或者直接使用正斜杠 / 作为路径分隔符,以避免跨平台问题和转义字符的困扰。修改为:
String path2 = dest.getPath() + File.separator + file.getName();
或者更简单地使用正斜杠:
String path2 = dest.getPath() + "/" + file.getName();
问题3:压缩文件夹需要递归,所以要把创建压缩包的代码放到递归外面进行实现。
所以刚开始我只是把压缩包所表示的路径放到主函数中了,然后压缩流还是放到递归中去,想着每次递归都创建一个新的压缩流,但实际并不是这样,因为压缩流并不是这样使用的。
// 目的文件夹
String path = "bbb";
// 压缩包的路径
File dest = new File(path, src.getName() + ".zip");
所以后面在运行时会报错。比如ZipOutputStream zos = new ZipOutputStream(new FileOutputStream("bbb\\bbb\\123"));就会出错,格式不正确,因为压缩流在创建时构造方法的参数需为.zip格式的字符串。
所以正确的其实应该放到递归方法的外面。
问题4:压缩方法的3个参数,刚开始我是这样写的,传递的是目标文件夹的路径,但是在实际运行过程中发现总是出现名字为aaa.zip这样的普通文件夹,一直也找不到问题所在,最终发现是对压缩流的new ZipEntry(path2)方法不熟悉,这个方法会在压缩路径的基础上创建文件夹的。
private static void zip(ZipOutputStream zos, File src, File dest) throws IOException {
...
String path2 = dest.getPath() + "\\" + file.getName();
// 转换为ZipEntry对象
ZipEntry zip = new ZipEntry(path2);
}
或者2个参数也行吧。 答案是不行,因为在使用new ZipEntry这个方法时要给出相对压缩包里面的完整路径路径,所以要用一个name变量进行存储。
问题5:文件夹是什么时候创建的?
答案是创建ZipEntry对象时new ZipEntry(path2);的参数,比如给定的path2的值为bbb/ccc/123.txt,那么就会在压缩包下的路径创建bbb/ccc/123.txt这么些个文化夹和文件,因为空的文件夹并不会被压缩。
下面是完整的代码实现:
public class ZIO3 {
public static void main(String[] args) throws IOException {
// 需求:把文件压缩到指定文件夹下
// 源文件夹
File src = new File("bbb//test");
// 目的文件夹
String path = "bbb";
// 压缩包的路径
File dest = new File(path, src.getName() + ".zip");
// 压缩流的路径
ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(dest));
zip(zos, src, src.getName());
zos.close();
}
private static void zip(ZipOutputStream zos, File src, String name) throws IOException {
File[] files = src.listFiles();
if (files != null) {
for (File file : files) {
if (file.isFile()) {
String path2 = name + "\\" + file.getName();
// 转换为ZipEntry对象
ZipEntry zip = new ZipEntry(path2);
zos.putNextEntry(zip);
FileInputStream fis = new FileInputStream(file);
int b;
while ((b = fis.read()) != -1) {
zos.write(b);
}
fis.close();
zos.closeEntry();
} else {
zip(zos, file, name + "\\" + file.getName());
}
}
}
}
}
平常压缩和解压都有软件了,这个还能干嘛,可能这个需要做页面下载文件用途。