136

全部

我创建了一个 jar 文件,其中包含以下 MANIFEST.MF:

Manifest-Version: 1.0
Ant-Version: Apache Ant 1.8.3
Created-By: 1.6.0_25-b06 (Sun Microsystems Inc.)
Main-Class: my.Main
Class-Path: . lib/spring-core-3.2.0.M2.jar lib/spring-beans-3.2.0.M2.jar

在它的根目录中有一个名为 my.config 的文件,它在我的 spring-context.xml 中被引用,如下所示:

<bean id="..." class="...">
    <property name="resource" value="classpath:my.config" />
</bean>

如果我运行 jar,一切看起来都很好,除了加载该特定文件:

Caused by: java.io.FileNotFoundException: class path resource [my.config] cannot be resolved to absolute file path because it does not reside in the file system: jar:file:/D:/work/my.jar!/my.config
        at org.springframework.util.ResourceUtils.getFile(ResourceUtils.java:205)
    at org.springframework.core.io.AbstractFileResolvingResource.getFile(AbstractFileResolvingResource.java:52)
    at eu.stepman.server.configuration.BeanConfigurationFactoryBean.getObject(BeanConfigurationFactoryBean.java:32)
    at eu.stepman.server.configuration.BeanConfigurationFactoryBean.getObject(BeanConfigurationFactoryBean.java:1)
    at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:142)
    ... 22 more
  • 类是从 jar 内部加载的
  • spring 和其他依赖项是从单独的 jar 中加载的
  • spring 上下文已加载(新 ClassPathXmlApplicationContext("spring-context/applicationContext.xml"))
  • my.properties 被加载到 PropertyPlaceholderConfigurer ("classpath:my.properties")
  • 如果我将 .config 文件放在文件系统之外,并将资源 url 更改为“file:”,一切似乎都很好......

有小费吗?

4

12 回答 12

259

如果您的 spring-context.xml 和 my.config 文件位于不同的 jar 中,那么您将需要使用classpath*:my.config?

更多信息在这里

另外,确保从 jar 文件中加载时使用resource.getInputStream()not 。resource.getFile()

于 2013-02-14T14:24:58.503 回答
68

我知道这个问题已经被回答了。但是,对于那些使用 Spring Boot 的人,这个链接帮助了我 - https://smarterco.de/java-load-file-classpath-spring-boot/

但是,resourceLoader.getResource("classpath:file.txt").getFile();导致此问题和 sbk 的评论:

就是这样。java.io.File 代表文件系统上的一个文件,在一个目录结构中。Jar 是一个 java.io.File。但是该文件中的任何内容都超出了 java.io.File 的范围。就java而言,在解压之前,jar文件中的类与word文档中的单词没有什么不同。

帮助我理解为什么要使用它getInputStream()。它现在对我有用!

谢谢!

于 2017-01-26T16:05:00.883 回答
61

在 spring jar 包中,我使用 new ClassPathResource(filename).getFile(),它会抛出异常:

无法解析为绝对文件路径,因为它不驻留在文件系统中:jar

但是使用new ClassPathResource(filename).getInputStream()会解决这个问题。原因是jar中的配置文件在操作系统的文件树中不存在,所以必须使用getInputStream().

于 2018-07-02T08:24:27.977 回答
8

错误消息是正确的(如果不是很有帮助):我们尝试加载的文件不是文件系统上的文件,而是 ZIP 中 ZIP 中的一大块字节。

通过实验(Java 11,Spring Boot 2.3.x),我发现它可以在不更改任何配置甚至通配符的情况下工作:

var resource = ResourceUtils.getURL("classpath:some/resource/in/a/dependency");
new BufferedReader(
  new InputStreamReader(resource.openStream())
).lines().forEach(System.out::println);
于 2020-11-06T16:04:22.823 回答
2

我在使用 Tomcat6.x 时遇到了类似的问题,我发现的任何建议都没有帮助。最后我删除work了(Tomcat的)文件夹,问题就消失了。

我知道这是不合逻辑的,但出于文档目的......

于 2014-11-24T12:49:32.353 回答
2

我在 Spring 应用程序中递归加载资源时遇到问题,发现问题是我应该使用resource.getInputStream. 这是一个示例,展示了如何递归地读取文件中的所有config/myfiles文件json

Example.java

private String myFilesResourceUrl = "config/myfiles/**/";
private String myFilesResourceExtension = "json";

ResourceLoader rl = new ResourceLoader();

// Recursively get resources that match. 
// Big note: If you decide to iterate over these, 
// use resource.GetResourceAsStream to load the contents
// or use the `readFileResource` of the ResourceLoader class.
Resource[] resources = rl.getResourcesInResourceFolder(myFilesResourceUrl, myFilesResourceExtension);

// Recursively get resource and their contents that match. 
// This loads all the files into memory, so maybe use the same approach 
// as this method, if need be.
Map<Resource,String> contents = rl.getResourceContentsInResourceFolder(myFilesResourceUrl, myFilesResourceExtension);

资源加载器.java

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.util.StreamUtils;

public class ResourceLoader {
  public Resource[] getResourcesInResourceFolder(String folder, String extension) {
    ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
    try {
      String resourceUrl = folder + "/*." + extension;
      Resource[] resources = resolver.getResources(resourceUrl);
      return resources;
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }

  public String readResource(Resource resource) throws IOException {
    try (InputStream stream = resource.getInputStream()) {
      return StreamUtils.copyToString(stream, Charset.defaultCharset());
    }
  }

  public Map<Resource, String> getResourceContentsInResourceFolder(
      String folder, String extension) {
    Resource[] resources = getResourcesInResourceFolder(folder, extension);

    HashMap<Resource, String> result = new HashMap<>();
    for (var resource : resources) {
      try {
        String contents = readResource(resource);
        result.put(resource, contents);
      } catch (IOException e) {
        throw new RuntimeException("Could not load resource=" + resource + ", e=" + e);
      }
    }
    return result;
  }
}
于 2019-11-18T16:18:46.607 回答
1

在我看来,@sbk 的答案是我们应该在 spring-boot 环境中执行此操作的方式(除了@Value("${classpath*:}))。但在我的场景中,如果从独立执行它就不起作用jar ..可能是我做错了什么。

但这可能是另一种方式,

InputStream is = this.getClass().getClassLoader().getResourceAsStream(<relative path of the resource from resource directory>);
于 2019-11-05T08:56:42.790 回答
1

我遇到了一个更复杂的问题,因为我有多个同名文件,一个位于主 Spring Boot jar 中,其他位于主 fat jar 中的 jar 中。我的解决方案是获取所有具有相同名称的资源,然后获取我需要按包名称过滤的资源。获取所有文件:

ResourceLoader resourceLoader = new FileSystemResourceLoader();
final Enumeration<URL> systemResources = resourceLoader.getClassLoader().getResources(fileNameWithoutExt + FILE_EXT);
于 2020-04-29T08:50:48.837 回答
1

对于 kotlin 用户,我是这样解决的:

val url = ResourceUtils.getURL("classpath:$fileName")
val response = url.openStream().bufferedReader().readText()
于 2021-10-07T20:54:51.763 回答
0

我遇到了同样的问题,最终使用了更方便的Guava Resources

Resources.getResource("my.file")
于 2020-06-03T10:40:36.147 回答
0

虽然这是一个非常古老的线程,但我在 Spring Boot 应用程序中添加 FCM 时也遇到了同样的问题。

在开发中,文件被打开并且没有错误,但是当我将应用程序部署到 AWS Elastic beanstalk 时,错误FileNotFoundException被抛出并且 FCM 无法正常工作。

所以这是我的解决方案,可以让它在开发环境和 jar 部署生产中工作。

我有一个组件类 FCMService,它的方法如下:

  @PostConstruct
  public void initialize() {

    log.info("Starting FCM Service");
    InputStream inputStream;

    try {

      ClassPathResource resource = new ClassPathResource("classpath:fcm/my_project_firebase_config.json");
      URL url = null;
      try {
        url = resource.getURL();
      } catch (IOException e) {

      }

      if (url != null) {
        inputStream = url.openStream();
      } else {
        File file = ResourceUtils.getFile("classpath:fcm/my_project_firebase_config.json");
        inputStream = new FileInputStream(file);
      }

      FirebaseOptions options = FirebaseOptions.builder().setCredentials(GoogleCredentials.fromStream(inputStream))
          .build();
      FirebaseApp.initializeApp(options);

      log.info("FCM Service started");

    } catch (IOException e) {
      log.error("Error starting FCM Service");
      e.printStackTrace();
    }

  }

希望这可以帮助寻求快速解决实施 FCM 的人。

于 2021-08-10T13:46:05.193 回答
0

在 Spring boot 1.5.22.RELEASE Jar 包装中,这对我有用:

InputStream resource = new ClassPathResource("example.pdf").getInputStream();
            

“example.pdf”在 src/main/resources 中。

然后将其读取为 byte[]

FileCopyUtils.copyToByteArray(resource);
于 2021-11-11T18:55:45.347 回答