如何从我的 jar 文件中访问资源文件夹中的文件夹?

Posted

技术标签:

【中文标题】如何从我的 jar 文件中访问资源文件夹中的文件夹?【英文标题】:How can I access a folder inside of a resource folder from inside my jar File? 【发布时间】:2012-06-16 06:56:25 【问题描述】:

我的项目根目录中有一个资源文件夹/包,我“不想”加载某个文件。如果我想加载某个文件,我会使用 class.getResourceAsStream 就可以了!我真正想要做的是在资源文件夹中加载一个“文件夹”,在该文件夹内的文件上循环并获取每个文件的流并读入内容......假设文件名在运行之前没有确定... 我该怎么办?有没有办法在你的 jar 文件中获取文件夹中的文件列表? 请注意,包含资源的 Jar 文件与运行代码的 jar 文件相同...

【问题讨论】:

这会有所帮助:***.com/questions/1529611/… 另见:***.com/questions/4764347/… 第一个是关于提取 jar 文件,这是我想尝试的最后一件事,就像一个 if-worse-comes-to-worse 的情况!!如果我想提取文件,我会在安装时将它们放在我的安装文件夹中!我想从 jar 中访问它们,我的原因是,如果 getResourceAsStream 可以访问文件,Java 也应该能够浏览该 jar 中的文件夹,而无需提取它!不过谢谢... 【参考方案1】:

如果您使用 Spring,您可以使用 org.springframework.core.io.support.PathMatchingResourcePatternResolver 并处理 Resource 对象而不是文件。这在 Jar 文件内部和外部运行时有效。

PathMatchingResourcePatternResolver r = new PathMatchingResourcePatternResolver();
Resource[] resources = r.getResources("/myfolder/*");

然后您可以使用getInputStream 和来自getFilename 的文件名访问数据。

请注意,如果您在从 Jar 运行时尝试使用 getFile,它仍然会失败。

【讨论】:

【参考方案2】:

以下代码从自定义资源目录获取 .yaml 文件。

ClassLoader classLoader = this.getClass().getClassLoader();
            URI uri = classLoader.getResource(directoryPath).toURI();
    
            if("jar".equalsIgnoreCase(uri.getScheme()))
                Pattern pattern = Pattern.compile("^.+" +"/classes/" + directoryPath + "/.+.yaml$");
                log.debug("pattern  ", pattern.pattern());
                ApplicationHome home = new ApplicationHome(SomeApplication.class);
                JarFile file = new JarFile(home.getSource());
                Enumeration<JarEntry>  jarEntries = file.entries() ;
                while(jarEntries.hasMoreElements())
                    JarEntry entry = jarEntries.nextElement();
                    Matcher matcher = pattern.matcher(entry.getName());
                    if(matcher.find())
                        InputStream in =
                                file.getInputStream(entry);
                        //work on the stream
    
    
                    
                
    
            else
                //When Spring boot application executed through Non-Jar strategy like through IDE or as a War.
    
                String path = uri.getPath();
                File[] files = new File(path).listFiles();
               
                for(File file: files)
                    if(file != null)
                        try 
                            InputStream is = new FileInputStream(file);
                           //work on stream
                            
                         catch (Exception e) 
                            log.error("Exception while parsing file yaml file  :  " , file.getAbsolutePath(), e.getMessage());
                        
                    else
                        log.warn("File Object is null while parsing yaml file");
                    
                
            
             

【讨论】:

【参考方案3】:

正如其他答案所指出的,一旦资源位于 jar 文件中,事情就会变得非常丑陋。在我们的例子中,这个解决方案:

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

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

这样我的应用程序中有简单的代码(没有技巧),并且我确定我没有忘记在列表中添加一个新条目,这要归功于测试。

【讨论】:

【参考方案4】:

以下代码将所需的“文件夹”作为路径返回,无论它是否在 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 是可关闭的,这意味着您必须在某个时候关闭它以释放系统资源。当然,返回的 Path 会立即失效。 请注意,如果是 jar 文件,则在创建文件系统时似乎会忽略资源名称(此处为 package + folder)。因此getPath 不返回folder/path/to/...,而只返回path/to/...【参考方案5】:

另一种解决方案,您可以像这样使用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())

【讨论】:

【参考方案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()));'用'路径 jarFile = Paths.get(jar.toURI());'让它在 Windows 中工作。还对 FileSystem 对象使用了 try with resource 语句。 这对我有用!我在 docker 中运行 dropwizard 应用程序。【参考方案7】:

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

【讨论】:

这很有效,除非你打算把事情搞砸,事实证明。 我认为这应该是公认的解决方案,因为它很干净并且不需要在单独的情况下处理 jar 和 IDE 版本。虽然我不确定为什么 getResource 找不到该文件夹​​,但 getResourceAsStream 可以。【参考方案8】:

在我的 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);

【讨论】:

您好,感谢您的回答,但是,您知道,在编写代码时,您需要知道该文件夹中每个文件的名称;你必须明确地命名每一个。我一直在寻找的是一种迭代文件夹内文件并在运行时获取它们的名称的方法,因此我可以在不更改代码的情况下向文件夹添加资源,因为我知道代码也会处理它们。不过,感谢您的时间。 :3【参考方案9】:

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

【讨论】:

如果它涉及 OSGI,我几乎不会称之为“简单”。但是,如果您碰巧已经在使用 OSGI……当然可以。但是为了解决这个特殊问题而建议转向 OSGI 似乎有点过分了。 OSGi 还解决了许多其他问题,现在它很容易上手。请参阅 OSGi enRoute 教程 除非您已经被要求使用它,否则我会远离 OSGi。过度设计的 Bloatware 解决了 90 年代 IMO 的部署问题。【参考方案10】:

尝试以下方法。 使资源路径"&lt;PathRelativeToThisClassFile&gt;/&lt;ResourceDirectory&gt;" E.g.如果您的类路径是 com.abc.package.MyClass 并且您的资源文件位于 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/");

【讨论】:

感谢您的时间和精力,但是当您的代码库位于 .jar 文件中并且您的资源也在该 .jar 文件中时,这不起作用。当您在 .jar 文件中时,您无法创建 File(),也无法获取该文件夹的 URL……我个人对此感到很平静,只是在 XML 中列出了每个文件……我没有根本不认为您可以对 jar 文件中的文件夹执行此操作... 抱歉,您遇到了问题。但是,当您的代码库位于 jar 文件中并且您的资源也在该 jar 文件中时,它确实有效。您没有在磁盘上创建 File() - 您正在将 jar 文件中的 File 读取到 java 内存中。请注意,ClassLoader 非常乐意将 jar 文件视为磁盘上的目录。以下是来源的详细信息: 刚刚在 jar 上运行代码并得到错误...哎呀。 V对不起,我的错。需要用“File:...!/dir1/dir2...”处理 jar URL 路径。两个修复:***.com/questions/1429172/… OR String jarPath = dirURL.getPath().substring(5, dirURL.getPath().indexOf("!")); JarFile jar = new JarFile(URLDecoder.decode(jarPath, "UTF-8"));枚举 条目 = jar.entries(); while(entries.hasMoreElements()) // 做点什么 从 javadoc 很明显,该方法并不总是保证在所有情况下都返回文件。但是,如果您实际阅读 Q,则 OP 可以 100% 保证通过构造处理文件和文件夹。 Q 要求访问目录和文件——这比 API 的广义抽象更具体。转换为文件是适当和正确的以满足需要。在发布如此强烈相反的 cmets 之前,请正确阅读 Q。干杯。 OP 无法处理文件和目录。他正在使用 JAR 文件中的资源。 JAR 文件中的资源不是文件或目录,不能与File 类一起列出。此代码不适用于 JAR 文件中的资源。期间。【参考方案11】:

终于找到了解决办法:

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 多个源文件的代码中......但它似乎是唯一的解决方案。谢谢。 仅供参考,如果从 webstart 启动,这将不起作用,因为 getPath 返回与服务器域相关的内容。 如果路径在 jar 中,不要认为它会起作用,在 toURI 转换时会出现此错误:java.lang.IllegalArgumentException: URI is not hierarchy 只适用于单jar文件,不能从另一个jar的依赖中提取资源 这不是一个安全的解决方案,因为: 1. 它假定 .jar 文件是同一系统上的本地文件; 2. URL.getPath() 没有返回一个有效的文件名,它只是返回 URL 的路径部分,所有的百分比转义都完好无损; 3.ProtectionDomain.getCodeSource() can return null.【参考方案12】:

link 告诉你怎么做。

神奇的是 getResourceAsStream() 方法:

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

【讨论】:

不,伙计!我不要一个文件!!我想要一个完整的文件夹,其子文件在运行前未确定!!!

以上是关于如何从我的 jar 文件中访问资源文件夹中的文件夹?的主要内容,如果未能解决你的问题,请参考以下文章

Java:如何在可运行的 Jar 文件中打包和访问资源?

如何从我的项目构建 jar 文件?

如何从我的 servlet 类访问放置在 /tmp 文件夹中的 sqlite db 文件?

如何从我的视图的 javascript 访问我的代码隐藏文件中的 JsonResult 变量?剃刀页面。 C#

从 jar 中读取资源文件

从我的项目中包含的 jar 调用属性文件