是否有API可以获取类路径资源(例如,我将从中获得的资源Class.getResource(String)
)java.nio.file.Path
?理想情况下,我想将新的Path
API与类路径资源一起使用。
是否有API可以获取类路径资源(例如,我将从中获得的资源Class.getResource(String)
)java.nio.file.Path
?理想情况下,我想将新的Path
API与类路径资源一起使用。
Answers:
这对我有用:
return Paths.get(ClassLoader.getSystemResource(resourceName).toURI());
Thread.currentThread().getContextClassLoader().getResource(resourceName).toURI()
猜测要执行的操作是,对来自类路径(可能是来自jar内)的资源调用Files.lines(...)。
由于Oracle通过使getResource如果位于jar文件中而不使其返回可用路径来混淆路径何时是路径的概念,因此您需要执行以下操作:
Stream<String> stream = new BufferedReader(new InputStreamReader(ClassLoader.getSystemResourceAsStream("/filename.txt"))).lines();
class.getResource
需要一个斜杠,但是在以斜杠getSystemResourceAsStream
为前缀时找不到该文件。
最通用的解决方案如下:
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);
}
});
然后,它将再次适用于所有版本和存储方法。
事实证明,您可以在内置的Zip文件系统提供程序的帮助下执行此操作。但是,直接Paths.get
将资源URI传递给是行不通的;相反,必须首先为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对象,但从未关闭它。最好的方法是传递类似于Consumer的工作对象,就像Holger的回答是如何做到的。打开ZipFS FileSystem足够长的时间,以便工作人员可以对Path进行任何操作(只要工作人员不尝试存储Path对象供以后使用),然后关闭FileSystem。
newFileSystem
可导致多个资源永远挂在开放式环境中。尽管@raisercostin附录在尝试创建已创建的文件系统时避免了该错误,但是如果尝试使用返回的内容Path
,则会得到ClosedFileSystemException
。@Holger的回复对我来说效果很好。
FileSystem
。如果您从Jar中加载资源,然后创建所需资源FileSystem
- FileSystem
还将允许您从同一Jar中加载其他资源。同样,一旦创建了新的,FileSystem
您就可以尝试再次使用来加载资源,Paths.get(Path)
并且实现将自动使用new FileSystem
。
#getPath(String)
在FileSystem
对象上使用方法。
在Java8中使用NIO从资源文件夹读取文件
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();
}
您需要定义文件系统以从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)) {
....
}
}
Paths.get(URI)
意味),然后有了URL.toURI(), and last
getResource(),它返回了一个URL
。您可能可以将它们链接在一起。还没有尝试过。