从jar中读取资源文件

Pri*_*CJC 207 java resources jar file embedded-resource

我想从我的jar中读取资源,如下所示:

File file;
file = new File(getClass().getResource("/file.txt").toURI());
BufferredReader reader = new BufferedReader(new FileReader(file));

//Read the file
Run Code Online (Sandbox Code Playgroud)

并且它在Eclipse中运行时工作正常,但是如果我将它导出到jar中运行它就会出现IllegalArgumentException:

Exception in thread "Thread-2"
java.lang.IllegalArgumentException: URI is not hierarchical
Run Code Online (Sandbox Code Playgroud)

而且我真的不知道为什么,但经过一些测试我发现如果我改变了

file = new File(getClass().getResource("/file.txt").toURI());
Run Code Online (Sandbox Code Playgroud)

file = new File(getClass().getResource("/folder/file.txt").toURI());
Run Code Online (Sandbox Code Playgroud)

然后它的工作正好相反(它适用于jar而不是eclipse).

我正在使用Eclipse,我的文件夹在一个类文件夹中.

Dre*_*nis 348

而不是尝试将资源作为文件来处理,而只是要求ClassLoader通过getResourceAsStream返回资源的InputStream:

InputStream in = getClass().getResourceAsStream("/file.txt"); 
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
Run Code Online (Sandbox Code Playgroud)

只要file.txt资源在类路径上可用,那么无论file.txt资源是在classes/目录中还是在内部,这种方法都将以相同的方式工作jar.

之所以URI is not hierarchical发生这种情况,是因为jar文件中资源的URI看起来像这样:file:/example.jar!/file.txt.您无法读取jar(zip文件)中的条目,就像它是一个普通的旧文件.

这可以通过以下答案得到很好的解释:

  • 如果资源不存在,`getResourceAsStream`返回null,这样就可以成为"存在"测试. (5认同)
  • 谢谢,这是非常有帮助的,代码工作得很好,但我确实有一个问题,我需要确定`InputStream`是否存在(如`File.exists()`)所以我的游戏可以判断是否使用默认值文件与否.谢谢. (3认同)
  • “你无法像读取普通的旧文件一样读取 jar(zip 文件)中的条目。”这很糟糕,因为有大量库函数需要文件路径作为输入。 (3认同)
  • 当然,...别忘了关闭inputStream和BufferedReader (2认同)

Juo*_*nis 18

要访问jar中的文件,您有两个选择:

  • 将文件放在与包名匹配的目录结构中(在解压缩.jar文件后,它应该与.class文件位于同一目录中),然后使用 getClass().getResourceAsStream("file.txt")

  • 将文件放在根目录下(在解压缩.jar文件后,它应该在根目录中),然后使用它来访问它 Thread.currentThread().getContextClassLoader().getResourceAsStream("file.txt")

当jar用作插件时,第一个选项可能不起作用.


小智 9

我之前遇到过这个问题,我做了后备加载方式。基本上第一种方式在 .jar 文件中工作,第二种方式在 eclipse 或其他 IDE 中工作。

public class MyClass {

    public static InputStream accessFile() {
        String resource = "my-file-located-in-resources.txt";

        // this is the path within the jar file
        InputStream input = MyClass.class.getResourceAsStream("/resources/" + resource);
        if (input == null) {
            // this is how we load file within editor (eg eclipse)
            input = MyClass.class.getClassLoader().getResourceAsStream(resource);
        }

        return input;
    }
}
Run Code Online (Sandbox Code Playgroud)


Nav*_*eth 7

问题是某些第三方库需要文件路径名而不是输入流。大多数答案都没有解决这个问题。

在这种情况下,一种解决方法是将资源内容复制到临时文件中。以下示例使用 jUnit 的TemporaryFolder.

    private List<String> decomposePath(String path){
        List<String> reversed = Lists.newArrayList();
        File currFile = new File(path);
        while(currFile != null){
            reversed.add(currFile.getName());
            currFile = currFile.getParentFile();
        }
        return Lists.reverse(reversed);
    }

    private String writeResourceToFile(String resourceName) throws IOException {
        ClassLoader loader = getClass().getClassLoader();
        InputStream configStream = loader.getResourceAsStream(resourceName);
        List<String> pathComponents = decomposePath(resourceName);
        folder.newFolder(pathComponents.subList(0, pathComponents.size() - 1).toArray(new String[0]));
        File tmpFile = folder.newFile(resourceName);
        Files.copy(configStream, tmpFile.toPath(), REPLACE_EXISTING);
        return tmpFile.getAbsolutePath();
    }
Run Code Online (Sandbox Code Playgroud)

  • 由于 `resourceName` 应始终使用 `/` 作为分隔符,与 `decomposePath` 内的 `File` 操作不同,后者使用系统特定的文件分隔符,因此与 `path.split("/" 相比,该方法不仅不必要地复杂)`,这甚至是不正确的。此外,不清楚为什么在不使用其结果时调用 .toArray(new String[0]))`。 (3认同)

Nao*_*Bar 5

到目前为止(2017 年 12 月),这是我发现的唯一在 IDE 内部和外部都有效的解决方案。

使用PathMatchingResourcePatternResolver

注意:它也适用于 spring-boot

在这个例子中,我正在读取位于src/main/resources/my_folder 中的一些文件:

try {
    // Get all the files under this inner resource folder: my_folder
    String scannedPackage = "my_folder/*";
    PathMatchingResourcePatternResolver scanner = new PathMatchingResourcePatternResolver();
    Resource[] resources = scanner.getResources(scannedPackage);

    if (resources == null || resources.length == 0)
        log.warn("Warning: could not find any resources in this scanned package: " + scannedPackage);
    else {
        for (Resource resource : resources) {
            log.info(resource.getFilename());
            // Read the file content (I used BufferedReader, but there are other solutions for that):
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(resource.getInputStream()));
            String line = null;
            while ((line = bufferedReader.readLine()) != null) {
                // ...
                // ...                      
            }
            bufferedReader.close();
        }
    }
} catch (Exception e) {
    throw new Exception("Failed to read the resources folder: " + e.getMessage(), e);
}
Run Code Online (Sandbox Code Playgroud)