我如何从jar文件中获取资源“文件夹”?


139

我在项目的根目录中有一个资源文件夹/程序包,我“不想”加载某个文件。如果我想加载某个文件,我将使用class.getResourceAsStream,我会很好的!我实际上想要做的是在资源文件夹中加载一个“文件夹”,循环访问该文件夹中的文件,并获取每个文件的流,然后读取内容...假定在运行时之前未确定文件名... 我该怎么办?有没有一种方法可以获取jar文件中“文件夹”中文件的列表?请注意,带有资源的Jar文件与从中运行代码的Jar文件相同。

提前致谢...




5
第一个是关于提取jar文件的,这是我要尝试的最后一件事,就像如果情况变得更糟!如果要提取文件,则只需在安装时将它们放到我的安装文件夹中即可!我想从jar中访问它们,而我的原因是,如果getResourceAsStream可以访问File,则Java也应该能够浏览该jar中的文件夹,而无需提取它!但是,谢谢…
Mostafa Zeinali 2012年

Answers:


111

最后,我找到了解决方案:

final String path = "sample/folder";
final File jarFile = new File(getClass().getProtectionDomain().getCodeSource().getLocation().getPath());

if(jarFile.isFile()) {  // Run with JAR file
    final JarFile jar = new JarFile(jarFile);
    final Enumeration<JarEntry> entries = jar.entries(); //gives ALL entries in jar
    while(entries.hasMoreElements()) {
        final String name = entries.nextElement().getName();
        if (name.startsWith(path + "/")) { //filter according to the path
            System.out.println(name);
        }
    }
    jar.close();
} else { // Run with IDE
    final URL url = Launcher.class.getResource("/" + path);
    if (url != null) {
        try {
            final File apps = new File(url.toURI());
            for (File app : apps.listFiles()) {
                System.out.println(app);
            }
        } catch (URISyntaxException ex) {
            // never happens
        }
    }
}

第二个块仅在IDE上运行应用程序时有效(不适用于jar文件),如果不喜欢,可以将其删除。


更新了正确答案。尽管这不是我想要做的,尤其是在具有34000+个源文件的代码中……但这似乎是唯一的解决方案。谢谢。
Mostafa Zeinali

4
仅供参考,如果从Webstart启动,则此方法不起作用,因为getPath返回相对于服务器域的某些内容。
amos

1
不要认为路径在罐子中会起作用,这会在toURI转换时产生此错误:java.lang.IllegalArgumentException:URI不是分层的
tribbloid

4
仅适用于单个jar文件,不适用于从另一个jar中的依赖项中提取资源
Tribbloid

2
这不是一个安全的解决方案,因为:1.它假定.jar文件是同一系统上的本地文件;2. URL.getPath()不返回有效的文件名,它仅返回URL的路径部分,所有百分号转义均保持不变。3. ProtectionDomain.getCodeSource()可以返回null
VGR 17-10-10

14

尝试以下方法。 例如,如果您的类路径为com.abc.package.MyClass并且
资源"<PathRelativeToThisClassFile>/<ResourceDirectory>"文件位于src / com / abc / package / resources /中,则输入资源路径:

URL url = MyClass.class.getResource("resources/");
if (url == null) {
     // error - missing folder
} else {
    File dir = new File(url.toURI());
    for (File nextFile : dir.listFiles()) {
        // Do something with nextFile
    }
}

您也可以使用

URL url = MyClass.class.getResource("/com/abc/package/resources/");

27
感谢您的时间和精力,但是当您的代码库位于.jar文件中并且您的资源也位于该.jar文件中时,此方法将无效。当您位于.jar文件中时,无法创建File(),也无法获取该文件夹的URL ...我个人对此很满意,只是以XML列出了每个文件...认为您根本无法对jar文件中的文件夹执行此操作……
Mostafa Zeinali 2012年

2
抱歉,您遇到了问题。但是,当您的代码库位于jar文件中并且资源也位于该jar文件中时,它确实可以工作。您没有在磁盘上创建File()-您正在将jar文件中的File读取到Java内存中。请注意,ClassLoader非常乐意对待与磁盘上目录等效的jar文件。这是来源的详细信息:
Glen Best

1
只需在罐子上运行代码,并得到错误...糟糕。V对不起,我不好。需要使用“ File:...!/ dir1 / dir2 ...”处理jar URL路径。修复了两个问题:stackoverflow.com/questions/1429172/… 或字符串jarPath = dirURL.getPath()。substring(5,dirURL.getPath()。indexOf(“!”)));JarFile jar = new JarFile(URLDecoder.decode(jarPath,“ UTF-8”)); Enumeration <JarEntry>条目= jar.entries(); while(entries.hasMoreElements()){//做某事}
Glen Best

3
从javadoc中可以明显看出,该方法始终不能保证在所有情况下都返回文件。但是,如果您实际阅读了Q,则可以保证OP 100%可以通过构造处理文件和文件夹。Q要求获得对目录和文件的访问权限-这比API的广义抽象更具体。转换为文件是适当且正确的以满足需求。在发布此类强烈相反的评论之前,请正确阅读Q。干杯。
2014年

7
OP无法处理文件和目录。他正在使用JAR文件中的资源。JAR文件中的资源不是文件或目录,因此无法与File该类一起列出。此代码不适用于JAR文件中的资源。期。
罗恩侯爵

7

我知道这是很多年前的事。但是,对于其他人来说,遇到这个话题。您可以做的是getResourceAsStream()在目录路径中使用method,并且输入Stream将具有该目录中的所有文件名。之后,您可以使用每个文件名连接目录路径,并在循环中为每个文件调用getResourceAsStream。


7
事实证明,这很有效,除非您计划破坏事物。
Tustin2121 '18

1
我认为这应该是公认的解决方案,因为它很干净,不需要在单独的情况下处理jar和IDE版本。虽然我不确定为什么getResource找不到文件夹,但是getResourceAsStream可以找到文件夹。
Raghuram Onti Srinivasan

6

在尝试从jar中打包的资源中加载某些hadoop配置时,我手头也遇到了同样的问题……在IDE和jar(发行版)上。

我发现java.nio.file.DirectoryStream最好的方法是遍历本地文件系统和jar上的目录内容。

String fooFolder = "/foo/folder";
....

ClassLoader classLoader = foofClass.class.getClassLoader();
try {
    uri = classLoader.getResource(fooFolder).toURI();
} catch (URISyntaxException e) {
    throw new FooException(e.getMessage());
} catch (NullPointerException e){
    throw new FooException(e.getMessage());
}

if(uri == null){
    throw new FooException("something is wrong directory or files missing");
}

/** i want to know if i am inside the jar or working on the IDE*/
if(uri.getScheme().contains("jar")){
    /** jar case */
    try{
        URL jar = FooClass.class.getProtectionDomain().getCodeSource().getLocation();
        //jar.toString() begins with file:
        //i want to trim it out...
        Path jarFile = Paths.get(jar.toString().substring("file:".length()));
        FileSystem fs = FileSystems.newFileSystem(jarFile, null);
        DirectoryStream<Path> directoryStream = Files.newDirectoryStream(fs.getPath(fooFolder));
        for(Path p: directoryStream){
            InputStream is = FooClass.class.getResourceAsStream(p.toString()) ;
        performFooOverInputStream(is);
        /** your logic here **/
            }
    }catch(IOException e) {
        throw new FooException(e.getMessage());     
    }
}
else{
    /** IDE case */
    Path path = Paths.get(uri);
    try {
        DirectoryStream<Path> directoryStream = Files.newDirectoryStream(path);
        for(Path p : directoryStream){
            InputStream is = new FileInputStream(p.toFile());
            performFooOverInputStream(is);
        }
    } catch (IOException _e) {
        throw new FooException(_e.getMessage());
    }
}

这对我来说很好。只有我切换了'path jarFile = Paths.get(jar.toString()。substring(“ file:”。length()));' 带有'Path jarFile = Paths.get(jar.toURI());' 使它在Windows中工作。还对FileSystem对象使用了try with resource语句。
贾勒雅各布森

这对我有用!我正在docker中运行dropwizard应用程序。
nIKUNJ

4

以下代码将所需的“文件夹”作为Path返回,而不管它是否在jar中。

  private Path getFolderPath() throws URISyntaxException, IOException {
    URI uri = getClass().getClassLoader().getResource("folder").toURI();
    if ("jar".equals(uri.getScheme())) {
      FileSystem fileSystem = FileSystems.newFileSystem(uri, Collections.emptyMap(), null);
      return fileSystem.getPath("path/to/folder/inside/jar");
    } else {
      return Paths.get(uri);
    }
  }

需要Java 7+。


真好!不过,我必须补充一点,即FileSystem是Closeable的,这意味着您必须在某个时候将其关闭以释放系统资源。当然,此后返回的Path将立即失效。
Kirill Gamazkov '18

请注意,如果是jar文件,则在创建文件系统时似乎忽略了资源名称(此处为package + folder)。因此getPath不返回folder/path/to/...,而仅返回path/to/...
Marcono1234 '19

1

另一个解决方案,您可以ResourceLoader像这样使用:

import org.springframework.core.io.Resource;
import org.apache.commons.io.FileUtils;

@Autowire
private ResourceLoader resourceLoader;

...

Resource resource = resourceLoader.getResource("classpath:/path/to/you/dir");
File file = resource.getFile();
Iterator<File> fi = FileUtils.iterateFiles(file, null, true);
while(fi.hasNext()) {
    load(fi.next())
}

0

简单...使用OSGi。在OSGi中,您可以使用findEntries和findPaths遍历Bundle的条目。


10
如果涉及OSGI,我几乎不会称其为“简单”。但是,如果您碰巧已经在使用OSGI ...是的。但是建议仅使用OSGI来解决此特定问题似乎有点过多。
克里斯(Kris)2013年

OSGi还解决了许多其他问题,如今它很容易上手。请参阅OSGi enRoute教程
Peter Kriens

除非您已经被要求使用它,否则我将远离OSGi。经过精心设计的Bloatware解决了90年代IMO的部署问题。
user2337270

-1

在我的jar文件中,我有一个名为Upload的文件夹,此文件夹中还有其他三个文本文件,我需要在jar文件之外有一个完全相同的文件夹和文件,我使用下面的代码:

URL inputUrl = getClass().getResource("/upload/blabla1.txt");
File dest1 = new File("upload/blabla1.txt");
FileUtils.copyURLToFile(inputUrl, dest1);

URL inputUrl2 = getClass().getResource("/upload/blabla2.txt");
File dest2 = new File("upload/blabla2.txt");
FileUtils.copyURLToFile(inputUrl2, dest2);

URL inputUrl3 = getClass().getResource("/upload/blabla3.txt");
File dest3 = new File("upload/Bblabla3.txt");
FileUtils.copyURLToFile(inputUrl3, dest3);

1
嗨,谢谢您的回答,但是您知道,编写代码时需要知道该文件夹中每个文件的名称。而且您必须明确命名每个人。我正在寻找的是一种在文件夹内的文件上进行迭代并在运行时获取其名称的方法,因此我可以在不更改代码的情况下向文件夹添加资源,因为知道代码也会对其进行处理。谢谢您的宝贵时间。:3
Mostafa Zeinali

-1

正如其他答案指出的那样,一旦资源位于jar文件中,事情就会变得很丑陋。在我们的情况下,此解决方案:

https://stackoverflow.com/a/13227570/516188

在测试中效果很好(因为运行测试时,代码未打包在jar文件中),但在应用程序实际正常运行时不起作用。所以我要做的是...我在应用程序中对文件列表进行了硬编码,但是我有一个测试可以从磁盘读取实际列表(可以这样做,因为它可以在测试中工作),并且如果实际列表没有与应用返回的列表不匹配。

这样,我的应用程序中就有了简单的代码(没有技巧),并且我确定我不会忘记通过测试在列表中添加了新条目。


-25

链接告诉您如何。

魔术是getResourceAsStream()方法:

InputStream is = 
this.getClass().getClassLoader().getResourceAsStream("yourpackage/mypackage/myfile.xml")

20
没有花花公子!我不要一个文件!!我想要一个完整的文件夹,其子文件在运行前尚未确定!!!
Mostafa Zeinali 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.