罐子里的文件在春天是看不见的



所有

我创建了一个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-content.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:",那么一切似乎都很好

有什么建议吗?

如果您的spring-content.xml和my.config文件位于不同的jar中,则需要使用classpath*:my.config

更多信息点击这里

此外,请确保从jar文件内部加载时使用的是resource.getInputStream()而不是resource.getFile()

在spring-jar包中,我使用了新的ClassPathResource(filename).getFile(),它抛出了异常:

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

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

我知道这个问题已经得到了回答。然而,对于那些使用弹簧靴的人来说,这个链接帮助了我——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无法访问的注意,在解压缩之前,jar文件中的类是no与word文档中的单词不同。

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

谢谢!

错误消息是正确的(如果不是很有用的话):我们试图加载的文件不是文件系统上的文件,而是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);

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

示例.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);

ResourceLoader.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;
  }
}

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

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

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

val url = ResourceUtils.getURL("classpath:$fileName")
val response = url.openStream().bufferedReader().readText()

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

但这可能是另一种方法,

InputStream is = this.getClass().getClassLoader().getResourceAsStream(<relative path of the resource from resource directory>);

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

ResourceLoader resourceLoader = new FileSystemResourceLoader();
final Enumeration<URL> systemResources = resourceLoader.getClassLoader().getResources(fileNameWithoutExt + FILE_EXT);

在Spring boot 1.5.22.RELEASE Jar包装中,这对我来说很有效:

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

"example.pdf";位于src/main/resources中。

然后将其读取为字节[]

FileCopyUtils.copyToByteArray(resource);

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

Resources.getResource("my.file")

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

在开发过程中,文件被打开,没有任何错误,但当我将应用程序部署到AWS Elastic beanstall时,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的快速解决方案的人。

可以像一样处理

var serviceAccount = ClassLoader.getSystemResourceAsStream(FB_CONFIG_FILE_NAME);
FirebaseOptions options = new FirebaseOptions.Builder()
  .setCredentials(GoogleCredentials.fromStream(serviceAccount))
  .build();

其中FB_CONFIG_FILE_NAME是"资源"文件夹中的文件名。

最新更新