176

是否有一个 API 可以将类路径资源(例如我从中得到的Class.getResource(String))作为java.nio.file.Path? 理想情况下,我想将花哨的新PathAPI 与类路径资源一起使用。

4

8 回答 8

225

这个对我有用:

return Paths.get(ClassLoader.getSystemResource(resourceName).toURI());
于 2014-05-14T11:59:15.290 回答
29

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

由于 Oracle 混淆了 a 何时Path为 a的概念,如果它位于 jar 文件中,Path则不返回可用路径,因此您需要做的是这样的事情:getResource

Stream<String> stream = new BufferedReader(new InputStreamReader(ClassLoader.getSystemResourceAsStream("/filename.txt"))).lines();
于 2015-09-15T06:57:37.693 回答
13

最通用的解决方案如下:

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);
        }
    }
}

主要的障碍是处理这两种可能性,要么拥有我们应该使用但不关闭的现有文件系统(如使用fileURI 或 Java 9 的模块存储),要么必须自己打开并安全关闭文件系统(如zip/jar 文件)。

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

它可以用于上面提到的所有 Java 版本,例如将包的内容(java.lang在示例中)列为Paths,如下所示:

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);
    }
});

做同样的事情。


The final release of Java 9’s module system has broken the above code example. The Java versions from 9 to 12 inconsistently return the path /java.base/java/lang/Object.class for Paths.get(Object.class.getResource("Object.class")) whereas it should be /modules/java.base/java/lang/Object.class. This can be fixed by prepending the missing /modules/ when the parent path is reported as non-existent:

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);
    }
});

Then, it will again work with all versions and storage methods. Starting with JDK 13, this work-around is not necessary anymore.

于 2016-03-15T20:10:47.947 回答
11

事实证明,您可以在内置Zip 文件系统提供程序的帮助下做到这一点。但是,将资源 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 对象但从未关闭它。最好的方法是传递一个像消费者一样的工作对象,就像 Holger 的答案是如何做到的。打开 ZipFS 文件系统,让工作人员对 Path 做任何需要做的事情(只要工作人员不尝试存储 Path 对象供以后使用),然后关闭文件系统。

于 2013-03-30T11:26:26.643 回答
5

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

public static Path getResourcePath(Class<?> resourceClass, String resourceName) throws URISyntaxException {
    URL url = resourceClass.getResource(resourceName);
    return Paths.get(url.toURI());
}  
于 2014-09-03T19:42:14.983 回答
2

Read a File from resources folder using NIO, in 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();
    }
于 2018-03-16T06:26:28.800 回答
1

You can not create URI from resources inside of the jar file. You can simply write it to the temp file and then use it (java8):

Path path = File.createTempFile("some", "address").toPath();
Files.copy(ClassLoader.getSystemResourceAsStream("/path/to/resource"), path, StandardCopyOption.REPLACE_EXISTING);
于 2016-10-05T18:54:20.763 回答
0

您需要定义文件系统以从 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)) {
            ....
        }
    }
于 2015-12-02T10:26:28.090 回答