以前我曾用兩個類(ZipItem
和 ZipSystem
)實(shí)現(xiàn)了一個簡單的 ZIP 文件系統(tǒng)(以下簡稱 ZFS)。其實(shí)這兩個小類挺好用的,而且支持嵌套的 ZIP 文件,但是,但是……JDK 7 丟下來一枚叫做 NIO2 的笑氣炸彈,引入了一套標(biāo)準(zhǔn)的文件系統(tǒng) API,我承認(rèn)我中彈了,手癢了,又根據(jù)這套 API 重新實(shí)現(xiàn)了 ZIP 文件系統(tǒng),終于在今天初步完工,哈。
話說,JDK 7 其實(shí)捆綁銷售了一個 ZFS,demo 目錄下還有源代碼。可……它達(dá)不到我的奢求,而且 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 // 當(dāng)成 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 沒有經(jīng)過代碼審閱、沒有經(jīng)過測試、沒有經(jīng)過……然后,@author Xueming Shen
,真是丟咱華夏民族的臉……
下面列個表格詳細(xì)比較官方 ZFS 和山寨 ZFS:
比較內(nèi)容 | 官方 ZFS | 山寨 ZFS |
實(shí)現(xiàn)方式 | 另起爐灶,用純 Java 重新實(shí)現(xiàn)了對 ZIP 文件格式的處理代碼。 | 基于 ZipFile 和 ZipInputStream 這兩個已經(jīng)穩(wěn)定多年的類,但涉及了大量本地代碼調(diào)用,也許會影響性能。 |
讀操作 | 支持,且通過解壓到臨時文件支持隨機(jī)訪問。 | 支持,但不支持隨機(jī)訪問。 |
寫操作 | 通過解壓到臨時文件進(jìn)行支持,但無法檢測到其他進(jìn)程對同一個 ZIP 文件的寫操作,不適用于并發(fā)環(huán)境。 | 不支持。ZIP 文件事實(shí)上是一個整體,對內(nèi)部條目的任何修改都可能導(dǎo)致重構(gòu)整個文件,因此所謂的寫操作必須通過臨時文件來處理,效率低下,意義不大,而且難以處理嵌套 ZIP 文件。這也符合我的原則:不解壓。 |
嵌套 ZIP 文件 | 不支持。 | 支持,當(dāng)然讀取嵌套 ZIP 文件會慢一些。 |
反斜線分隔符 | 不支持,直接瓜掉。 | 支持,且和標(biāo)準(zhǔn)的斜線分隔符區(qū)別對待。例如,/abc/ 和 /abc\ 算不同的文件,實(shí)際上這兩個能夠并存于 ZIP 文件中。 |
空目錄名 | 不支持,直接瓜掉。 | 支持。例如 /a/b 和 /a//b 是兩個可以并存且不同的文件。 |
山寨 ZFS 的用法示例:
Map<String, Object> env = new HashMap<>(); // 用于解碼 ZIP 條目名。默認(rèn)為 Charset.defaultCharset()。 env.put("charset", StandardCharsets.UTF_8); // 指示是否自動探測嵌套的 ZIP 文件。默認(rèn)為 false。 env.put("autoDetect", true); // 默認(rèn)目錄,用于創(chuàng)建和解析相對路徑。默認(rèn)為“/”。 env.put("defaultDirectory", "/dir/"); // 從文件創(chuàng)建一個 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")) { // 創(chuàng)建一個嵌套的 ZFS。 try (FileSystem nestedZfs = zfs.provider().newFileSystem(path, env)) { // 此處省略若干行。 } } }
最后雙手奉上源代碼:請猛擊此處!