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

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

【问题讨论】:

  • 第一个是关于提取 jar 文件,这是我想尝试的最后一件事,就像一个 if-worse-comes-to-worse 的情况!!如果我想提取文件,我会在安装时将它们放在我的安装文件夹中!我想从 jar 中访问它们,我的原因是,如果 getResourceAsStream 可以访问文件,Java 也应该能够浏览该 jar 中的文件夹,而无需提取它!不过谢谢...

标签: java list resources directory


【解决方案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://stackoverflow.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 路径。两个修复:stackoverflow.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")
          

          【讨论】:

          • 不,伙计!我不要一个文件!!我想要一个完整的文件夹,其子文件在运行前未确定!!!
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2023-04-10
          • 2015-09-19
          • 2012-12-07
          • 2011-12-18
          • 2020-05-27
          • 1970-01-01
          相关资源
          最近更新 更多