一、JDK內置操作Zip文件
其實,在JDK中已經存在操作ZIP的工具類:ZipInputStream。
基本使用:
public static Map<String, String> readZipFile(String file) throws Exception {
Map<String, String> resultMap = new HashMap<String, String>();
Charset gbk = Charset.forName("GBK");
ZipFile zf = new ZipFile(file, gbk); // 此處可以用無Charset的構造函數,但是即使是設置為GBK也是處理不了中文的,后面會再說
InputStream in = new BufferedInputStream(new FileInputStream(file));
ZipInputStream zin = new ZipInputStream(in);
ZipEntry ze;
while ((ze = zin.getNextEntry()) != null) {
if (ze.isDirectory()) {
} else {
long size = ze.getSize(); // 文件的大小
String name = ze.getName(); // 獲取文件名稱
// 具體對其中每個文件的操作和獲取信息,可以參考JDK API
if (size > 0) {
InputStream inputStream = zf.getInputStream(ze); // 拿到文件流
// …… 業務邏輯
}
}
}
zin.closeEntry();
return resultMap;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
更多的操作可以參考其他人的總結,本文的重點在於描述JDK自帶zip操作API的不便之處,從而引出Zip4J。
JDK自帶ZIP API有個非常重大的問題:不支持ZIP中目錄中的中文。如下異常:
java.lang.IllegalArgumentException: MALFORMED
at java.util.zip.ZipCoder.toString(Unknown Source)
at java.util.zip.ZipInputStream.readLOC(Unknown Source)
at java.util.zip.ZipInputStream.getNextEntry(Unknown Source)
at com.mskj.test.md5.BigFileMD5.readZipFile(BigFileMD5.java:131)
at com.mskj.test.md5.BigFileMD5.checkFileMD5(BigFileMD5.java:42)
at com.mskj.test.TestMain.main(TestMain.java:12)
1
2
3
4
5
6
7
我的ZIP中目錄:“Test\Test-01\Te\你好\……”,當然導致該異常的原因不只是中文的問題,但是大部分情況下都是。對於JDK自帶的ZIP API還有一些其他問題,比如操作解壓和壓縮API接口不方便(帶有密碼ZIP)等。當然,網上有許多人說是已經可以解決中文的問題,但是比較麻煩,並不是每次都能夠成功,我本地也有嘗試過。
在我們的項目中,通過不斷的比較,最終還是選擇了ZIP4J。
二、ZIP4J
官網:http://www.lingala.net/zip4j/
zip4j默認采用的是UTF-8編碼,所以本身支持中文(但是,個人建議還是在讀取zip文件后,立即設置字符集),同時也支持密碼,而且支持多種壓縮算法,可以說功能強大,但使用起來卻非常簡單,當然,如果有其他需求,需要自己從官網上看API。如果你百度或者谷歌ZIP4J的用法,會有許多好的博客和教程,再此不再贅述。
例如:
https://blog.csdn.net/ditto_zhou/article/details/82977395
https://www.cnblogs.com/622698abc/p/3389855.html
https://rensanning.iteye.com/blog/1836727
……
我想結合一下我項目中需求以及網上許多同仁的問題(不解壓zip文件,直接通過InputStream的形式讀取其中的文件信息),說一個簡單的應用:不解壓ZIP文件的前提下,直接利用流(InuptStream)形式讀取其中的文件,並讀取文件的MD5值。
類似於JDK自帶ZipInputStream的形式讀取zip文件,由於ZIP4J的ZipInputStream不具備ZipInputStream.getNextEntry()),所以,在ZIP4J中只能通過FileHeader來進行循環。而且,JDK自帶API中獲取ZIP其中的文件流InputStream時,需要:
ZipFile zf = new ZipFile(file);
InputStream inputStream = zf.getInputStream(ZipEntry);
1
2
所以,對應ZIP4J就只能ZipInputStream(該類時InputStream的子類)。
具體代碼如下:
import java.util.List;
import net.lingala.zip4j.core.ZipFile;
import net.lingala.zip4j.io.ZipInputStream;
import net.lingala.zip4j.model.FileHeader;
public class ZIP4JUtils {
/**
* @param file
* @throws Exception
*/
public static void readZipFileMD5ByZip4J(String file) throws Exception {
file.replaceAll("\\\\", "/");
ZipFile zFile = new ZipFile(file);
// 此處最好立即設置字符集
zFile.setFileNameCharset("GBK");
if (!zFile.isValidZipFile()) {
return ;
}
// 獲取ZIP中所有文件的FileHeader,以便后面對zip中文件進行遍歷
List<FileHeader> list = zFile.getFileHeaders();
// 此時list的size包括:文件夾、子文件夾、文件的個數
System.out.println(list.size());
// 遍歷其中的文件
for (FileHeader fileHeader : list) {
String fileName = fileHeader.getFileName();
// fileName會將目錄單獨讀出來,而且帶有路徑分割符
if (fileName.endsWith("/") || fileName.endsWith("\\\\") || fileName.endsWith("\\")) {
System.out.println(fileName + " 這是一個文件夾。");
continue;
}else {
ZipInputStream inputStream = zFile.getInputStream(fileHeader);
String Md5String = BigFileMD5.getStreamMD5(inputStream);
System.out.println(fileName + " 這是一個文件,該文件的MD5值:" + Md5String);
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
其中計算MD5值的類如下:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.codec.binary.Hex;
public class BigFileMD5 {
static MessageDigest MD5 = null;
static List<File> list = new ArrayList<File>();
static{
try{
MD5 = MessageDigest.getInstance("MD5");
}catch(NoSuchAlgorithmException e){
e.printStackTrace();
}
}
/**
* 對一個文件獲取md5值
* @return md5串
*/
public static String getStreamMD5(InputStream fileInputStream) {
try {
byte[] buffer = new byte[8192];
int length;
while ((length = fileInputStream.read(buffer)) != -1) {
MD5.update(buffer, 0, length);
}
return new String(Hex.encodeHex(MD5.digest()));
} catch (FileNotFoundException e) {
e.printStackTrace();
return null;
} catch (IOException e) {
e.printStackTrace();
return null;
} finally {
try {
if (fileInputStream != null)
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 對一個文件獲取md5值
* @return md5串
*/
public static String getMD5(File file) {
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream(file);
byte[] buffer = new byte[8192];
int length;
while ((length = fileInputStream.read(buffer)) != -1) {
MD5.update(buffer, 0, length);
}
return new String(Hex.encodeHex(MD5.digest()));
} catch (FileNotFoundException e) {
e.printStackTrace();
return null;
} catch (IOException e) {
e.printStackTrace();
return null;
} finally {
try {
if (fileInputStream != null)
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
輸出結果:
Test-01/ 這是一個文件夾。
Test-01/BigFielMD5.jar 這是一個文件,該文件的MD5值:db55e6677f7087754f11eaf84b2d728e
Test-01/Te/ 這是一個文件夾。
Test-01/Te/Test.txt 這是一個文件,該文件的MD5值:c6354a0eb36fac331c138eec7a4826ef
Test-01/Te/你好/ 這是一個文件夾。
Test-01/Te/你好/Hello/ 這是一個文件夾。
Test-01/Te/你好/Hello/antlr-2.7.2.jar 這是一個文件,該文件的MD5值:a73459120df5cadf75eaa98453433a01
Test-01/Te/你好/Hello/antlr-2.7.2.jar.sha1 這是一個文件,該文件的MD5值:3aa5bc052867b339a0b6ed0546f9b336
Test-01/Te/你好/Hello/antlr-2.7.2.pom 這是一個文件,該文件的MD5值:b1136da0c12ce8ffc18d00f8742256ee
Test-01/Te/你好/Hello/antlr-2.7.2.pom.sha1 這是一個文件,該文件的MD5值:ebd38323fc24c8aab361b2a7660ec666
Test-01/測試.txt 這是一個文件,該文件的MD5值:0b60aa5d9aa241ca6063495086e38e95
1
2
3
4
5
6
7
8
9
10
11
這樣就實現了,Java不解壓直接讀取zip文件和文件內容(InputStream的形式)。
如有不對之處,還請指教!
---------------------