壓縮和解壓
實現了:
- 可以壓縮文件,也可以壓縮文件夾
- 同時支持壓縮多級文件夾,工具內部做了遞歸處理
- 碰到空的文件夾,也可以壓縮
代碼
/*ZipUtils.java*/
import java.io.*;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
/**
* @author : yanjundong
* @date : 2020-03-10 13:19
* @description : zip的壓縮與解壓
*/
public class ZipUtils {
private static final int BUFFER_SIZE = 2 * 1024;
/**
* zip解壓
* @param srcFile zip源文件
* @param destDirPath 解壓後的目標文件夾
* @throws RuntimeException 解壓失敗會拋出運行時異常
*/
public static void unZip(File srcFile, String destDirPath) throws RuntimeException {
long start = System.currentTimeMillis();
// 判斷源文件是否存在
if (!srcFile.exists()) {
throw new RuntimeException(srcFile.getPath() + "所指文件不存在");
}
// 開始解壓
try (ZipFile zipFile = new ZipFile(srcFile)) {
Enumeration<?> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = (ZipEntry) entries.nextElement();
// 如果是文件夾,就創建個文件夾
if (entry.isDirectory()) {
String dirPath = destDirPath + File.separator + entry.getName();
File dir = new File(dirPath);
dir.mkdirs();
} else {
// 如果是文件,就先創建一個文件,然後用io流把內容copy過去
File targetFile = new File(destDirPath + File.separator + entry.getName());
// 保證這個文件的父文件夾必須要存在
if(!targetFile.getParentFile().exists()){
targetFile.getParentFile().mkdirs();
}
targetFile.createNewFile();
// 將壓縮文件內容寫入到這個文件中
InputStream is = zipFile.getInputStream(entry);
FileOutputStream fos = new FileOutputStream(targetFile);
int len;
byte[] buf = new byte[BUFFER_SIZE];
while ((len = is.read(buf)) != -1) {
fos.write(buf, 0, len);
}
// 關流順序,先打開的後關閉
fos.close();
is.close();
}
}
long end = System.currentTimeMillis();
System.out.println("解壓完成,耗時:" + (end - start) +" ms");
} catch (Exception e) {
throw new RuntimeException("unzip error from ZipUtils", e);
}
}
/**
* 壓縮成ZIP 方法
* @param srcPath 需要壓縮的文件路徑
* @param outPath 壓縮文件的輸出路徑
* @throws RuntimeException 壓縮失敗會拋出運行時異常
*/
public static void toZip(String srcPath , String outPath) {
long start = System.currentTimeMillis(); //開始時間
File sourceFile = new File(srcPath);
File outFile = new File(outPath);
try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(outFile))) {
//默認保存原來的目錄結構
compress(sourceFile, zos, sourceFile.getName(),true);
long end = System.currentTimeMillis();
System.out.println("壓縮完成,耗時:" + (end - start) +" ms");
} catch (Exception e) {
throw new RuntimeException("zip error from ZipUtils",e);
}
}
/**
* 遞歸壓縮方法
* @param sourceFile 源文件
* @param zos zip輸出流
* @param name 壓縮後的名稱
* @param KeepDirStructure 是否保留原來的目錄結構
* true:保留目錄結構;
* false:所有文件跑到壓縮包根目錄下(注意:不保留目錄結構可能會出現同名文件,會壓縮失敗)
* @throws Exception
*/
private static void compress(File sourceFile, ZipOutputStream zos, String name,
boolean KeepDirStructure) throws IOException {
byte[] buf = new byte[BUFFER_SIZE];
//是一個文件
if(sourceFile.isFile()){
// 向zip輸出流中添加一個zip實體,構造器中name爲zip實體的文件的名字
zos.putNextEntry(new ZipEntry(name));
// copy文件到zip輸出流中
int len;
InputStream in = new FileInputStream(sourceFile);
while ((len = in.read(buf)) != -1){
zos.write(buf, 0, len);
}
zos.closeEntry();
in.close();
} else {
//是一個目錄
File[] listFiles = sourceFile.listFiles();
if(listFiles == null || listFiles.length == 0){
// 需要保留原來的文件結構時,需要對空文件夾進行處理
if(KeepDirStructure){
// 空文件夾的處理
zos.putNextEntry(new ZipEntry(name + "/"));
// 沒有文件,不需要文件的copy
zos.closeEntry();
}
}else {
for (File file : listFiles) {
// 判斷是否需要保留原來的文件結構
if (KeepDirStructure) {
// 注意:file.getName()前面需要帶上父文件夾的名字加一斜槓,
// 不然最後壓縮包中就不能保留原來的文件結構,即:所有文件都跑到壓縮包根目錄下了
compress(file, zos, name + "/" + file.getName(),KeepDirStructure);
} else {
compress(file, zos, file.getName(),KeepDirStructure);
}
}
}
}
}
}
測試
import org.junit.Test;
import util.ZipUtils;
import java.io.*;
public class ZipTest {
@Test
public void m1() throws Exception {
String srcFiles = "/Users/zhang/Desktop/2020";
String outFile = "/Users/zhang/Downloads/1.zip";
ZipUtils.toZip(srcFiles, outFile);
}
@Test
public void m2() throws Exception {
File srcFile = new File("/Users/zhang/Downloads/1.zip");
String destDirPath = "/Users/zhang/Downloads/test/";
ZipUtils.unZip(srcFile,destDirPath);
}
}