以前我曾用两个类(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)) {
// 此处省略若干行。
}
}
}
最后双手奉上源代码:请猛击此处!