是否有API可以将java.nio.file.Path
的类路径资源(例如我从Class.getResource(String)
获得的内容)获得?理想情况下,我希望将新的Path API与类路径资源一起使用。
答案 0 :(得分:132)
这个适用于我:
return Paths.get(ClassLoader.getSystemResource(resourceName).toURI());
答案 1 :(得分:24)
猜测你想要做什么,就是在来自类路径的资源上调用Files.lines(...) - 可能来自jar中。
由于Oracle通过不让getResource返回一个可用的路径(例如它驻留在一个jar文件中)来解决Path是一个Path的概念,你需要做的是这样的事情:
Stream<String> stream = new BufferedReader(new InputStreamReader(ClassLoader.getSystemResourceAsStream("/filename.txt"))).lines();
答案 2 :(得分:9)
事实证明,在内置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);
}
<强>更新强>
正确地指出上面的代码包含资源泄漏,因为代码打开了一个新的FileSystem对象但从未关闭它。最好的方法是传递类似消费者的工作者对象,就像霍尔格的回答一样。打开ZipFS文件系统的时间足够长,以便工作人员可以对Path执行任何操作(只要worker不尝试存储Path对象供以后使用),然后关闭FileSystem。
答案 3 :(得分:9)
最通用的解决方案如下:
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 10.它探测是否已经存在打开的文件系统在打开一个新的之前,它也适用于应用程序的另一个组件已经为同一个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不一致地返回/java.base/java/lang/Object.class
的路径Object.class.getResource("Object.class")
,而它应该是/modules/java.base/java/lang/Object.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);
}
});
然后,它将再次适用于所有版本和存储方法。
答案 4 :(得分:4)
我写了一个小帮助方法来从你的类资源中读取Paths
。它非常方便使用,因为它只需要您存储资源的类的引用以及资源本身的名称。
public static Path getResourcePath(Class<?> resourceClass, String resourceName) throws URISyntaxException {
URL url = resourceClass.getResource(resourceName);
return Paths.get(url.toURI());
}
答案 5 :(得分:1)
您无法从jar文件内的资源创建URI。您只需将其写入临时文件,然后使用它(java8):
Path path = File.createTempFile("some", "address").toPath();
Files.copy(ClassLoader.getSystemResourceAsStream("/path/to/resource"), path, StandardCopyOption.REPLACE_EXISTING);
答案 6 :(得分:0)
您需要定义Filesystem以从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)) {
....
}
}
答案 7 :(得分:0)
使用NIO在java8中读取资源文件夹中的文件
public static String read(String fileName) {
Path path;
StringBuilder data = new StringBuilder();
Stream<String> lines = null;
try {
path = Paths.get(Thread.currentThread().getContextClassLoader().getResource(fileName).toURI());
lines = Files.lines(path);
} catch (URISyntaxException | IOException e) {
logger.error("Error in reading propertied file " + e);
throw new RuntimeException(e);
}
lines.forEach(line -> data.append(line));
lines.close();
return data.toString();
}