对于弹簧,罐子里的文件是不可见的

全部

我创建了一个 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 上下文(new ClassPathXmlApplicationContext (“ spring-context/applicationContext.xml”))
  • Properties 加载到 PropertyPlaceholderConfigrer (“ classspath: my.properties”)中
  • 如果我把我的。配置文件在文件系统之外,并将资源 URL 更改为“ file:”,一切似乎都很好..。

有什么建议吗?

153260 次浏览

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

更多信息 给你

另外,确保从 jar 文件中加载时使用的是 resource.getInputStream()而不是 resource.getFile()

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

我知道这不合逻辑,但为了记录..。

我知道这个问题已经得到了回答。但是,对于那些使用弹簧启动,这个链接帮助了我-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 文件中的类不是 不同于单词文档中的单词。

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

谢谢!

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

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

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

在我看来,@sbk 给出的答案是我们在 Spring-boot 环境中应该采用的方式(除了@Value (“ ${ classspath * : }”))。但是在我的场景中,如果从独立 jar 执行,它就不工作。.也许我做错了什么。

但这可以是另一种方式,

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

我在 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;
}
}

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

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

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

Resources.getResource("my.file")

错误消息是正确的(如果不是很有帮助的话) : 我们试图加载的文件是 没有,这是文件系统中的一个文件,但是是 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);

虽然这是一个非常老的线程,但我也面临着同样的问题,而添加在一个 Spring 启动应用程序的 FCM。

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

因此,下面是我的解决方案,可以让它在开发 env 和 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。

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

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

在 Spring boot 1.5.22. Relase Jar 中,这个打包方式对我很有效:

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

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

然后读取为 byte []

FileCopyUtils.copyToByteArray(resource);

可以这样处理:

var serviceAccount = ClassLoader.getSystemResourceAsStream(FB_CONFIG_FILE_NAME);


FirebaseOptions options = new FirebaseOptions.Builder()
.setCredentials(GoogleCredentials.fromStream(serviceAccount))
.build();

其中 FB _ CONFIG _ FILE _ NAME 是“ resources”文件夹中的文件名。