【问题标题】:NIO2: how to generically map a URI to a Path?NIO2:如何将 URI 映射到路径?
【发布时间】:2013-03-04 06:35:38
【问题描述】:

我正在尝试找到一种简单的方法来将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

注意URIs 是如何映射到Path 对象的,这些对象已经“植根”到正确的FileSystem 中,例如引用jar 中目录“/org/foo”的路径。

这段代码让我困扰的是,尽管 NIO2 很容易:

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

...没有很好的方法将 URI 映射到 new FileSystem 实例中的路径。

我能找到的最好的方法是,在创建文件系统之后,我可以要求它的FileSystemProvider 给我路径:

Path path = fs.provider().getPath(uri);

但这似乎是错误的,因为不能保证它会返回一个绑定到我刚刚实例化的文件系统的路径(即path.getFileSystem() == fs)。它非常依赖 FileSystemProvider 的内部状态来知道我指的是什么 FileSystem 实例。有没有更好的办法?

【问题讨论】:

    标签: java jar filesystems zip nio2


    【解决方案1】:

    您在 zipfs 的实现/文档中发现了一个错误。Path.get 方法的文档指出:

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

    edit:对于需要关闭的文件系统,最好要求程序员调用 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。

    【讨论】:

    • 我也想知道这是否是一个错误,但我认为使用FileSystems.newFileSystem 明确创建文件系统是有道理的。创建一个新的 zipfs 意味着打开底层的 zip 文件,这个文件最终必须被关闭。这就是FileSystem 实现Closeable 的原因。所以Paths.get(uri) 不自动打开zipfs 的原因是设计者希望程序员明确地完成zipfs FileSystem 对象的打开和关闭。至少,这是我的猜想。 :) 这就是我在上面的示例代码中使用 try-with-resources 的原因。
    • @cambecc 我改变了答案。区别在于“应该自动打开”和“可以自动打开”。一个
    • @cambecc 如果您依靠 Oracle 记录的 getFileSystem 行为仅返回以前打开的 FileSystems 表单 newFileSystem 您可以按照更改后的答案编写代码。这只会给关闭在不同位置/线程上使用的文件系统留下一种奇怪的感觉。
    • 是的,正是我希望可以避免的那种“奇怪的感觉”。就像在我原来的示例中一样,不能保证 Paths.get(uri) 会返回一个植根于上一行刚刚创建的 FileSystemPath
    • FileSystemProvider#getPath(URI) 的 javadoc 突出显示了问题的症结所在(顺便说一句,Paths.get(URI) 最终会调用它)。这是 javadoc 所说的: 通过转换给定的 URI 返回一个 Path 对象。生成的 Path 与已存在或自动构建的 FileSystem 相关联。 所以...如果该提供程序存在 19 个单独的 FileSystem 实例,它们中的哪一个与生成的 Path 对象相关联?我还没有找到明确选择要关联的FileSystem 实例的方法,所以我认为这是API 中的一个缺陷。
    【解决方案2】:

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

    答:没有这样的方法

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-10
      • 1970-01-01
      • 2012-05-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多