錯誤展示:
錯誤代碼:
import java.io.*;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
/**
* @author 何
* @date 2019-08-13 15:06
*/
public class MyZipUtil {
public static void main(String[] args) throws IOException {
testZip("D:\\zip\\", "D:\\zip\\test.zip", 0);
}
/**
*
* @param srcFilesAbsoluteParentPath 需要被壓縮的文件的絕對父路徑 例: D:\\zip 壓縮這個目錄下所有的文件
* @param targetZipFileAbsolutePath 需要壓縮到指定的位置的絕對路徑(含文件名) 例: D:\\zip\\test.zip 壓縮文件就放在這個位置
* @throws IOException
*/
public static void testZip(String srcFilesAbsoluteParentPath, String targetZipFileAbsolutePath, int depth)throws IOException{
//列出此目錄所有文件,遍歷操作
File parentPath = new File(srcFilesAbsoluteParentPath);
File[] files = parentPath.listFiles();
//創建一個Zip文件輸出流
ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(targetZipFileAbsolutePath));
for (int i = 0; i < files.length; i++) {
File currentFile = files[i];
//如果是目錄則遞歸調用此方法
if(currentFile.isDirectory()){
testZip(currentFile.getAbsolutePath()+"\\", targetZipFileAbsolutePath, depth+1);
continue;
}
/*
根據目錄深度,拼接zip文件裏面的各個深度的文件的層次結構
比如 在外面相對路徑是 dir1/dir2/test.txt ,這個時候的文件名test.txt是不可以直接拿來作爲entry的名字的,
應該根據深度,獲取他們的父目錄的名稱 拼接爲 dir1/dir2/test.txt這樣子才能保證層次結構不亂
*/
StringBuilder entryName = new StringBuilder(files[i].getName());
File tempFile = files[i];
for (int j = 0; j < depth; j++) {
tempFile = tempFile.getParentFile();
entryName.insert(0, tempFile.getName() + "\\");
}
zos.putNextEntry(new ZipEntry(entryName.toString()));
//根據此文件創建輸入流,用於等下文件的複製
FileInputStream fis = new FileInputStream(currentFile.getAbsolutePath());
//文件複製
byte[] bytes = new byte[1024];
int readedInt = 0;
while( (readedInt=fis.read(bytes, 0, bytes.length))!=-1){
zos.write(bytes, 0, readedInt);
}
fis.close(); //流要關閉
}
zos.finish();
zos.close();
}
}
執行前文件結構截圖
執行後的壓縮文件內部截圖
發現壓縮文件的時候,並沒有把更深層次的文件壓縮進去。
錯誤原因:
如圖可以看到,每次遞歸調用自身的時候,輸出流的引用都指向了的更新的對象。即“對象”被“重置”了。
大概文件夾目錄的文件雖然被正常壓縮進去了,可是被外圍的遞歸方法中產生的新的輸出流給 覆蓋 了。所以這就是爲什麼沒報錯,卻也沒有將子目錄裏面的文件壓縮進去的原因。(本來壓縮了,由於遞歸方法內我將輸出流重置的原因,他被沖洗掉了)
警惕:
慎用遞歸,在循環調用自身的時候,一定要想清楚,哪些是在變的,哪些是不在變的。像我這個例子就是沒有注意到 這個壓縮文件應該自始至終都是“一個輸出流”纔對
貼上改正後自己寫的Zip格式非加密 壓縮與解壓縮的工具類
ZipUtils.java
package com.zmedu.sdk.utils;
import org.junit.Test;
import java.io.*;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
/**
* @author 何
* @date 2019-08-13 15:06
*/
public class MyZipUtil {
public static void main(String[] args) throws IOException {
// testZip("D:\\zip\\", "D:\\zip\\test.zip", 0);
testUnzip("D:\\students\\students.zip", "D:\\students\\");
}
/**
*
* @param srcFilesAbsoluteParentPath 需要被壓縮的文件的絕對父路徑 例: D:\\zip 壓縮這個目錄下所有的文件
* @param targetZipFileAbsolutePath 需要壓縮到指定的位置的絕對路徑(含文件名) 例: D:\\zip\\test.zip 壓縮文件就放在這個位置
* @throws IOException
*/
public static void testZip(String srcFilesAbsoluteParentPath, String targetZipFileAbsolutePath, int depth)throws IOException{
//列出此目錄所有文件,遍歷操作
File parentPath = new File(srcFilesAbsoluteParentPath);
File[] files = parentPath.listFiles();
//創建一個Zip文件輸出流
ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(targetZipFileAbsolutePath));
for (int i = 0; i < files.length; i++) {
File currentFile = files[i];
//如果是目錄則遞歸調用此方法
if(currentFile.isDirectory()){
testZip(currentFile.getAbsolutePath()+"\\", targetZipFileAbsolutePath, depth+1);
continue;
}
/*
根據目錄深度,拼接zip文件裏面的各個深度的文件的層次結構
比如 在外面相對路徑是 dir1/dir2/test.txt ,這個時候的文件名test.txt是不可以直接拿來作爲entry的名字的,
應該根據深度,獲取他們的父目錄的名稱 拼接爲 dir1/dir2/test.txt這樣子才能保證層次結構不亂
*/
StringBuilder entryName = new StringBuilder(files[i].getName());
File tempFile = files[i];
for (int j = 0; j < depth; j++) {
tempFile = tempFile.getParentFile();
entryName.insert(0, tempFile.getName() + "\\");
}
zos.putNextEntry(new ZipEntry(entryName.toString()));
//根據此文件創建輸入流,用於等下文件的複製
FileInputStream fis = new FileInputStream(currentFile.getAbsolutePath());
//文件複製
byte[] bytes = new byte[1024];
int readedInt = 0;
while( (readedInt=fis.read(bytes, 0, bytes.length))!=-1){
zos.write(bytes, 0, readedInt);
}
fis.close(); //流要關閉
}
zos.finish();
zos.close();
}
/**
* 解壓Zip格式的非加密壓縮文件
* 提示:可以解壓子目錄的文件到對應的結構下,即這個壓縮文件的子目錄下還有孫子目錄,也能一併解壓
* 缺點:不能解壓裏面的zip文件
* 思路:可將每個 .zip 後綴的文件作爲一個ZipFile的實例對象,調用.size()方法,拋出
* java.util.zip.ZipException: error in opening zip file
* 上面異常的則表示這個是不能打開的壓縮文件,或者壓根就不是壓縮文件
* @param zipFileAbsolutePath 壓縮文件的絕對路徑
* @param destinationAbusolutePath 解壓目的地的絕對路徑
* @throws IOException
*/
public static void testUnzip(String zipFileAbsolutePath, String destinationAbusolutePath) throws IOException {
//根據壓縮文件創建一個文件輸入流,再創建一個Zip的專門的輸入流
FileInputStream fis = new FileInputStream(zipFileAbsolutePath); //"D:\\test\\test.zip"
ZipInputStream zis = new ZipInputStream(fis);
//可以當成壓縮文件裏面的條目
ZipEntry zipEntry = null;
//遍歷所有條目 ,本質上就是把他們全部給複製一份出來
while((zipEntry = zis.getNextEntry())!=null){
System.out.println("當前文件: " + zipEntry.getName() + ", 大小: " + zipEntry.getSize());
// ZipFile zipFile = new ZipFile("D:\\unzip\\FacePictures\\xxx\\Faces List.txt");
// System.out.println(zipFile.size());
//如果是目錄則建個目錄,不用自己去深度遍歷,zis.getNextEntry會自動獲取本壓縮文件下的所有文件
if(zipEntry.isDirectory()){
File file = new File(destinationAbusolutePath+zipEntry.getName());
file.mkdirs();
continue;
}
//創建一個輸出流,文件輸出流或者字節數組輸出流都行,二選一即可
File beCreateedFile = new File(destinationAbusolutePath+zipEntry.getName());
System.out.println("beCreateedFile: " + beCreateedFile);
if (!(beCreateedFile.getParentFile().exists())) {
beCreateedFile.getParentFile().mkdirs();
}
FileOutputStream fos = new FileOutputStream(beCreateedFile); //"D:\\test\\"
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
/* 第一種: 使用字節數組輸出流的方式 */
/*****************************************/
byte[] bytes = new byte[1024]; //長度隨意
int readedInt = 0;
while((readedInt = zis.read(bytes, 0, bytes.length)) != -1){
fos.write(bytes, 0, readedInt);
byteArrayOutputStream.write(bytes, 0, readedInt);
}
fos.close();
/*****************************************/
/* 第二種: 使用字節數組輸出流的方式 */
/*****************************************/
// byte[] imgBytes = byteArrayOutputStream.toByteArray();
// fos = new FileOutputStream(destinationAbusolutePath+zipEntry.getName());
// fos.write(imgBytes);
// fos.close();
/*****************************************/
zis.closeEntry(); // 文檔說要關閉,可是不關閉也可以,還是關閉把
}
//流要記得關閉!
zis.close();
fis.close();
}
}