2013-03-29 174 views
88

是否有API获取类路径资源(例如,我从Class.getResource(String)获得的内容)为java.nio.file.Path?理想情况下,我想用类路径资源来使用新奇的Path API。类路径资源的java.nio.file.Path

+3

好了,走长路(双关语意),你有'Paths.get(URI)',然后'URL.toURI ()',最后一个'getResource()'返回一个'URL'。你可以将这些链接在一起。虽然没有尝试过。 – NilsH

回答

95

这一次为我的作品:

return Paths.get(ClassLoader.getSystemResource(resourceName).toURI()); 
+32

这不适用于.jar文件中的资源。 – VGR

+3

@VGR如果.jar文件中的资源可以尝试这个'\t \t \t Resource resource = new ClassPathResource(“usage.txt”); \t \t \t BufferedReader reader = new BufferedReader(new InputStreamReader(resource.getInputStream()));'请参阅http://stackoverflow.com/questions/25869428/classpath-resource-not-found-when-running-as- jar – zhuguowei

8

事实证明,你可以在内置的Zip File System provider的帮助下做到这一点。但是,将资源URI直接传递给Paths.get将不起作用;相反,必须首先创建的jar URI一个zip文件系统没有条目名称,然后参考该文件系统中的条目:

static Path resourceToPath(URL resource) 
throws IOException, 
     URISyntaxException { 

    Objects.requireNonNull(resource, "Resource URL cannot be null"); 
    URI uri = resource.toURI(); 

    String scheme = uri.getScheme(); 
    if (scheme.equals("file")) { 
     return Paths.get(uri); 
    } 

    if (!scheme.equals("jar")) { 
     throw new IllegalArgumentException("Cannot convert to Path: " + uri); 
    } 

    String s = uri.toString(); 
    int separator = s.indexOf("!/"); 
    String entryName = s.substring(separator + 2); 
    URI fileURI = URI.create(s.substring(0, separator)); 

    FileSystem fs = FileSystems.newFileSystem(fileURI, 
     Collections.<String, Object>emptyMap()); 
    return fs.getPath(entryName); 
} 
+8

小心新创建的fs。使用同一个jar的第二个调用会抛出一个抱怨已经存在的文件系统的异常。 (FileSystem fs = ...){return fs.getPath(entryName);}或者如果你想让这个缓存做更高级的处理,会更好。目前的形式是有风险的。 – raisercostin

+1

除了潜在的未关闭的新文件系统的问题之外,关于方案之间关系的假设以及打开新文件系统的必要性以及URI内容的迷惑限制了解决方案的实用性。我已经设置了一个[新答案](http://stackoverflow.com/a/36021165/2711488),它显示了一种简化操作的通用方法,并同时处理新的Java 9类存储等新方案。它也适用于应用程序中的其他人已经打开文件系统(或者该方法对同一个jar被调用两次)... – Holger

+0

根据此解决方案的用法,非关闭的'newFileSystem'可能导致多个资源在打开永远。尽管@raisercostin附录在尝试创建已经创建的文件系统时避免了错误,但如果尝试使用返回的“Path”,将会得到“ClosedFileSystemException”。 @Holger响应对我来说效果很好。 –

3

我写了一个小助手方法从您的班级资源中读取Paths。使用它非常方便,因为它只需要您存储资源的类的引用以及资源本身的名称。

public static Path getResourcePath(Class<?> resourceClass, String resourceName) throws URISyntaxException { 
    URL url = resourceClass.getResource(resourceName); 
    return Paths.get(url.toURI()); 
} 
15

猜测你想要做的是调用Files.lines(...)对来自类路径的资源 - 可能来自jar中。

由于Oracle曲时的路径以不使的getResource如果它驻留在一个jar文件返回可用路径的路径的概念,你需要做的是这样的:

Stream<String> stream = new BufferedReader(new InputStreamReader(ClassLoader.getSystemResourceAsStream("/filename.txt"))).lines(); 
+1

是否需要前面的“/”在你的情况下,我不知道,但在我的情况'class.getResource'需要一个斜杠,但'getSystemResourceAsStream'找不到前缀斜杠的文件。 – Adam

7

最普遍的解决方案如下:

interface IOConsumer<T> { 
    void accept(T t) throws IOException; 
} 
public static void processRessource(URI uri, IOConsumer<Path> action) throws IOException { 
    try { 
     Path p=Paths.get(uri); 
     action.accept(p); 
    } 
    catch(FileSystemNotFoundException ex) { 
     try(FileSystem fs = FileSystems.newFileSystem(
       uri, Collections.<String,Object>emptyMap())) { 
      Path p = fs.provider().getPath(uri); 
      action.accept(p); 
     } 
    } 
} 

的主要障碍是应对两种可能,要么,具有现有我们应该使用但不关闭的文件系统(比如file URI或即将推出的Java 9模块存储),或者必须自己打开并安全地关闭文件系统(如zip/jar文件)。

因此,上面的解决方案将实际操作封装在interface中,处理这两种情况,之后在第二种情况下安全地关闭,并从Java 7运行到Java 9.它在打开文件系统之前探测是否已经有打开的文件系统新的,所以它也适用于应用程序的另一个组件已经为相同的zip/jar文件打开文件系统的情况。

它可以在上面提到的所有Java版本中使用,例如,到上市的包装的(在本例java.lang)的内容作为Path S,这样的:

processRessource(Object.class.getResource("Object.class").toURI(), new IOConsumer<Path>() { 
    public void accept(Path path) throws IOException { 
     try(DirectoryStream<Path> ds = Files.newDirectoryStream(path.getParent())) { 
      for(Path p: ds) 
       System.out.println(p); 
     } 
    } 
}); 

在Java 8或较新的,则可以使用lambda表达式或方法的引用来表示实际的动作,例如

processRessource(Object.class.getResource("Object.class").toURI(), path -> { 
    try(Stream<Path> stream = Files.list(path.getParent())) { 
     stream.forEach(System.out::println); 
    } 
}); 

也这样做。


Java 9模块系统的最终版本打破了上面的代码示例。JRE对于Object.class.getResource("Object.class")不一致返回路径/java.base/java/lang/Object.class,而它应该是/modules/java.base/java/lang/AbstractMethodError.class。这可以通过在前面加上缺少/modules/固定当父路径报告为不存在的:

processRessource(Object.class.getResource("Object.class").toURI(), path -> { 
    Path p = path.getParent(); 
    if(!Files.exists(p)) 
     p = p.resolve("/modules").resolve(p.getRoot().relativize(p)); 
    try(Stream<Path> stream = Files.list(p)) { 
     stream.forEach(System.out::println); 
    } 
}); 

然后,它会再次与所有版本和存储方法的工作。

1

您不能从jar文件内的资源创建URI。你可以简单地将其写入到临时文件,然后使用它(java8):

Path path = File.createTempFile("some", "address").toPath(); 
Files.copy(ClassLoader.getSystemResourceAsStream("/path/to/resource"), path, StandardCopyOption.REPLACE_EXISTING);