NIO2:如何将URI一般映射到路径?

时间:2013-03-04 06:35:39

标签: java jar filesystems zip nio2

我正在尝试找到一种简单的方法,可以将URI映射到Path,而无需编写特定于任何特定文件系统的代码。以下似乎有效,但需要一个有问题的技术:

public void process(URI uri) throws IOException {
    try {
        // First try getting a path via existing file systems. (default fs)
        Path path = Paths.get(uri);
        doSomething(uri, path);
    }
    catch (FileSystemNotFoundException e) {
        // No existing file system, so try creating one. (jars, zips, etc.)
        Map<String, ?> env = Collections.emptyMap();
        try (FileSystem fs = FileSystems.newFileSystem(uri, env)) {
            Path path = fs.provider().getPath(uri);  // yuck :(
            // assert path.getFileSystem() == fs;
            doSomething(uri, path);
        }
    }
}

private void doSomething(URI uri, Path path) {
    FileSystem fs = path.getFileSystem();
    System.out.println(uri);
    System.out.println("[" + fs.getClass().getSimpleName() + "] " + path);
}

在几个示例上运行此代码会产生以下结果:

file:/C:/Users/cambecc/target/classes/org/foo
[WindowsFileSystem] C:\Users\cambecc\target\classes\org\foo

jar:file:/C:/Users/cambecc/bin/utils-1.0.jar!/org/foo
[ZipFileSystem] /org/foo

注意URI如何映射到已经“扎根”到正确类型Path的{​​{1}}个对象,就像引用目录“/ org /的路径”一样foo“在罐子里面。

令我困扰的是这个代码是因为虽然NIO2很容易:

  • 将URI映射到现有文件系统中的路径:FileSystem
  • 将URI映射到 Paths.get(URI)实例:FileSystem

...没有很好的方法将URI映射到 new FileSystems.newFileSystem(uri, env)实例中的Path。

我能找到的最好的是,在创建了FileSystem后,我可以让它FileSystem给我路径:

FileSystemProvider

但这似乎是错误的,因为无法保证它会返回绑定到我刚刚实例化的FileSystem的Path(即Path path = fs.provider().getPath(uri); )。它几乎依赖于FileSystemProvider的内部状态来了解我所指的FileSystem实例。有没有更好的办法?

2 个答案:

答案 0 :(得分:3)

您在zipf的实现/文档中发现了一个错误.Path.get方法的文档声明:

* @throws  FileSystemNotFoundException
*          The file system, identified by the URI, does not exist and
*          cannot be created automatically

编辑:对于需要关闭的FileSystems,最好要求程序员调用newFileSystem以便他可以关闭它。文档应该更好地阅读“如果不应该创建它”。

ZipFs从不尝试创建新的文件系统。失败的get()未被捕获,但在尝试newFileSystem调用之前传递给调用者。参见来源:

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");
    return getFileSystem(uri).getPath(spec.substring(sep + 1));
}

换句话说:

Paths.get()
对于基于nio2的所有文件系统,

应该足够了。 使用zipfs设计。

Path path;
try {
   path = Paths.get( uri );
} catch ( FileSystemNotFoundException exp ) {
   try( FileSystem fs = FileSystems.newFileSystem( uri, Collections.EMPTY_MAP )) {;
       path = Paths.get( uri );
       ... use path ...
   }
}   

是您的解决方法的缩写形式。

注意:nio文档声明getFileSystem必须使用/返回匹配的newFileSystem创建的FileSystems。

答案 1 :(得分:2)

问:“我正在尝试找到一种简单的方法将URI映射到路径,而无需编写特定于任何特定文件系统的代码”

答:没有这样的方式

如果与URI关联的文件系统尚未打开,即当getFileSystem(在Paths.get中)抛出FileSystemNotFoundException时,整个问题才有意思。 但是要调用newFileSystem,你需要知道两件事:

  • 用于创建新文件系统的URI(部分)。文档说明,即在默认文件系统的情况下,URI的路径组件必须是根。例如getFileSystem(URI.create(“file:/// duda”,Collections.EMPTY_MAP)失败。
  • 在环境地图中设置的内容,例如可能是密码。

因此,要从URI创建新的文件系统,您必须了解要创建的文件系统。