用于类路径资源的java.nio.file.Path

时间:2013-03-29 23:41:25

标签: java java-7 nio2

是否有API可以将java.nio.file.Path的类路径资源(例如我从Class.getResource(String)获得的内容)获得?理想情况下,我希望将新的Path API与类路径资源一起使用。

8 个答案:

答案 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();
    }