以前我曾用兩個類(ZipItem
和 ZipSystem
)實現了一個簡單的 ZIP 文件系統(以下簡稱 ZFS)。其實這兩個小類挺好用的,而且支持嵌套的 ZIP 文件,但是,但是……JDK 7 丟下來一枚叫做 NIO2 的笑氣炸彈,引入了一套標準的文件系統 API,我承認我中彈了,手癢了,又根據這套 API 重新實現了 ZIP 文件系統,終于在今天初步完工,哈。
話說,JDK 7 其實捆綁銷售了一個 ZFS,demo 目錄下還有源代碼。可……它達不到我的奢求,而且 BUG 不少。隨便逮兩個:
// com.sun.nio.zipfs.ZipFileSystemProvider 類中的方法
@Override
public Path getPath(URI uri) {
String spec = uri.getSchemeSpecificPart();
int sep = spec.indexOf("!/");
if (sep == -1)
throw new IllegalArgumentException("URI: "
+ uri
+ " does not contain path info ex. jar:file:/c:/foo.zip!/BAR");
// 難怪該方法始終拋 IllegalArgumentException 異常,原來你小子把文件的 URI
// 當成 ZFS 的 URI 在用……
return getFileSystem(uri).getPath(spec.substring(sep + 1));
}
// com.sun.nio.zipfs.ZipFileSystem 類中的方法
@Override
public PathMatcher getPathMatcher(String syntaxAndInput) {
int pos = syntaxAndInput.indexOf(':');
// 丫的,pos == syntaxAndInput.length()?!誰寫的?抓出來鞭尸。
if (pos <= 0 || pos == syntaxAndInput.length()) {
throw new IllegalArgumentException();
很明顯,官方 ZFS 沒有經過代碼審閱、沒有經過測試、沒有經過……然后,@author Xueming Shen
,真是丟咱華夏民族的臉……
下面列個表格詳細比較官方 ZFS 和山寨 ZFS:
比較內容 |
官方 ZFS |
山寨 ZFS |
實現方式 |
另起爐灶,用純 Java 重新實現了對 ZIP 文件格式的處理代碼。 |
基于 ZipFile 和 ZipInputStream 這兩個已經穩定多年的類,但涉及了大量本地代碼調用,也許會影響性能。 |
讀操作 |
支持,且通過解壓到臨時文件支持隨機訪問。 |
支持,但不支持隨機訪問。 |
寫操作 |
通過解壓到臨時文件進行支持,但無法檢測到其他進程對同一個 ZIP 文件的寫操作,不適用于并發環境。 |
不支持。ZIP 文件事實上是一個整體,對內部條目的任何修改都可能導致重構整個文件,因此所謂的寫操作必須通過臨時文件來處理,效率低下,意義不大,而且難以處理嵌套 ZIP 文件。這也符合我的原則:不解壓。 |
嵌套 ZIP 文件 |
不支持。 |
支持,當然讀取嵌套 ZIP 文件會慢一些。 |
反斜線分隔符 |
不支持,直接瓜掉。 |
支持,且和標準的斜線分隔符區別對待。例如,/abc/ 和 /abc\ 算不同的文件,實際上這兩個能夠并存于 ZIP 文件中。 |
空目錄名 |
不支持,直接瓜掉。 |
支持。例如 /a/b 和 /a//b 是兩個可以并存且不同的文件。 |
山寨 ZFS 的用法示例:
Map<String, Object> env = new HashMap<>();
// 用于解碼 ZIP 條目名。默認為 Charset.defaultCharset()。
env.put("charset", StandardCharsets.UTF_8);
// 指示是否自動探測嵌套的 ZIP 文件。默認為 false。
env.put("autoDetect", true);
// 默認目錄,用于創建和解析相對路徑。默認為“/”。
env.put("defaultDirectory", "/dir/");
// 從文件創建一個 ZFS。
try (FileSystem zfs = FileSystems.newFileSystem(
URI.create("zip:" + Paths.get("docs.zip").toUri()), env)) {
Path path = zfs.getPath("app.jar");
if ((Boolean) Files.getAttribute(path, "isZip")) {
// 創建一個嵌套的 ZFS。
try (FileSystem nestedZfs = zfs.provider().newFileSystem(path, env)) {
// 此處省略若干行。
}
}
}
最后雙手奉上源代碼:請猛擊此處!