类path资源的java.nio.file.Path
有没有一个API来获得一个类path资源(例如,我从Class.getResource(String)
)作为一个java.nio.file.Path
? 理想情况下,我想用classpath资源来使用新奇的Path API。
这个为我工作:
return Paths.get(ClassLoader.getSystemResource(resourceName).toURI());
猜测你想要做什么,是调用Files.lines(…)在来自classpath的资源上 – 可能来自jar中。
由于Oracle通过不让getResource返回一个可用的path(如果它驻留在一个jar文件中),使得Path错误地认为Path是一个path,你需要做的是这样的:
Stream<String> stream = new BufferedReader(new InputStreamReader(ClassLoader.getSystemResourceAsStream("/filename.txt"))).lines();
事实certificate,你可以做到这一点,在内置的Zip文件系统提供商的帮助下。 但是,将资源URI直接传递给Paths.get
将不起作用; 相反,必须首先为不带条目名称的jar URI创buildzip文件系统,然后引用该文件系统中的条目:
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); }
最一般的解决scheme如下:
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); } } }
主要的障碍是要处理两种可能性,一种是我们应该使用的现有文件系统,但不能closures(比如使用file
URI或即将到来的Java 9模块存储),或者不得不自己打开文件系统,从而安全地closures文件系统(如zip / jar文件)。
因此,上面的解决scheme将实际动作封装在一个interface
,处理这两种情况,然后在第二种情况下安全地closures,并且从Java 7到Java 9进行工作。它在打开一个新文件之前探测是否已经有打开的文件系统,它也适用于您的应用程序的另一个组件已经为相同的zip / jar文件打开文件系统的情况。
它可以在上面提到的所有Java版本中使用,比如将一个包的内容(在这个例子中是java.lang
)列举为Path
,如下所示:
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或更新版本,可以使用lambdaexpression式或方法引用来表示实际的操作,例如
processRessource(Object.class.getResource("Object.class").toURI(), path -> { try(Stream<Path> stream = Files.list(path.getParent())) { stream.forEach(System.out::println); } });
做同样的事情。
我写了一个小的帮助器方法来从您的类资源中读取Paths
。 使用它非常方便,因为它只需要您存储资源的类的引用以及资源本身的名称。
public static Path getResourcePath(Class<?> resourceClass, String resourceName) throws URISyntaxException { URL url = resourceClass.getResource(resourceName); return Paths.get(url.toURI()); }
您需要将文件系统定义为从jar文件读取资源,如https://docs.oracle.com/javase/8/docs/technotes/guides/io/fsp/zipfilesystemprovider.html中所述; 。 我成功从以下代码的jar文件中读取资源:
Map<String, Object> env = new HashMap<>(); try (FileSystem fs = FileSystems.newFileSystem(uri, env)) { Path path = fs.getPath("/path/myResource"); try (Stream<String> lines = Files.lines(path)) { .... } }
您不能从jar文件中的资源创buildURI。 您可以简单地将它写入临时文件然后使用它(java8):
Path path = File.createTempFile("some", "address").toPath(); Files.copy(ClassLoader.getSystemResourceAsStream("/path/to/resource"), path, StandardCopyOption.REPLACE_EXISTING);