163

All

I created a jar file with the following MANIFEST.MF inside:

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

In its root there is a file called my.config which is referenced in my spring-context.xml like this:

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

If I run the jar, everything looks fine escept the loading of that specific file:

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
  • classes are loaded the from inside the jar
  • spring and other dependencies are loaded from separated jars
  • spring context is loaded (new ClassPathXmlApplicationContext("spring-context/applicationContext.xml"))
  • my.properties is loaded into PropertyPlaceholderConfigurer ("classpath:my.properties")
  • if I put my .config file outside the file system, and change the resource url to 'file:', everything seems to be fine...

Any tips?

BTakacs
  • 2,397
  • 3
  • 24
  • 26

13 Answers13

289

If your spring-context.xml and my.config files are in different jars then you will need to use classpath*:my.config?

More info here

Also, make sure you are using resource.getInputStream() not resource.getFile() when loading from inside a jar file.

sbk
  • 3,856
  • 1
  • 19
  • 19
  • 1
    They are in the same jar, but I tried your solution with the same result: java.io.FileNotFoundException: class path resource [classpath*:my.config] cannot be resolved to URL because it does not exist – BTakacs Feb 15 '13 at 07:57
  • I tried to move the file inside the 'conf' directory, but it did not help. (The documentation says: "This means that a pattern like "classpath*:*.xml" will not retrieve files from the root of jar files but rather only from the root of expanded directories. This originates from a limitation in the JDK's ClassLoader.getResources() method which only returns file system locations for a passed-in empty string (indicating potential roots to search).") – BTakacs Feb 15 '13 at 08:20
  • Weird thing is: my.properties is loaded by PropertyPlaceholderConfigurer and log4j.properties has been found by sl4j... and they are in the same location... Just this file... – BTakacs Feb 15 '13 at 16:35
  • 22
    Looking again, some of your calling code (possibly BeanConfigurationFactoryBean) is trying to load a java.io.File. File refers to files on the filesystem, which entries in a jar are not. The calling code should be using resource.getInputStream instead to load out of a jar. – sbk Feb 18 '13 at 11:44
  • 80
    ...and THIS is the answer... Thanks! Inside a jar do not use resource.getFile() :-) – BTakacs Feb 18 '13 at 13:00
  • 3
    any chance there is a "why?" behind not using getFile() inside of a jar? Is it simply that the file is inside of the Jar and hence the "file" is the jar file?? – RockMeetHardplace Feb 25 '13 at 01:08
  • 14
    That's it. A java.io.File represents a file on the file system, in a directory structure. The Jar is a java.io.File. But anything within that file is beyond the reach of java.io.File. As far as java is concerned, until it is uncompressed, a class in jar file is no different than a word in a word document. – sbk Feb 26 '13 at 12:43
  • When the file is into a jar, you must use getClass().getResourceAsStream() – ajaristi Jan 03 '17 at 03:20
  • In `resource.getInputStream()` , what is `resource` over here? – Shashi Ranjan Jul 11 '17 at 10:18
  • Can anybody post complete code to read/load a file which is inside a jar? – Shashi Ranjan Jul 11 '17 at 10:29
  • 1
    For anyone wondering, I used Apache commons IOUtils.toString(inputStream, "UTF-8") to retrieve the text from the InputStream - very clean – Geyser14 May 09 '19 at 22:59
  • @ShashiRanjan file contents for a file *within* a JAR must be read via `java.io.InputStream` of the file (via `java.io.File` is not possible when the file is *within* a JAR). Once you accept that, then it makes sense why this Answer on a similar question (using `InputStream`) has the [code](https://stackoverflow.com/a/22097153/1357094) to do so. – cellepo May 27 '19 at 23:42
  • 4
    Instead of resource.getInpuStream(), you can also use resource.getURL() – Valeriy K. Jul 04 '19 at 09:05
  • 1
    @ValeriyK. hands down most underrated comment lol :D, this fixed my issue, instead of spring framework's org.springframework.util.ResourceUtils.getFile() i Used org.springframework.util.ResourceUtils.getUrl() and no more exception... – solujic Aug 19 '20 at 10:42
  • Am I the only one scratching my head on why someone would store a file in jar to then painfully pull into your code as an InputStream? What is the ultimate objective? Would perhaps serialization be the better answer? As a framework, Spring should take the responsibility of either 1) supporting getFile() and 2) not allow any nonsensical workaround like getInputStrrem() . Personally, I would roll my own code for this as a service. That way I can provide for any number of options and prioritize. – Net Dawg Oct 15 '21 at 17:03
  • This advice is invaluable. I read as a file and the JAR can't see the folder and files. This is solved with using the `resource.getInputStream()` – Arefe Feb 18 '22 at 05:23
  • Hi @solujic do you know how to create a file while my spring application is in the docker container? If you can answer my question, thanks!! – Sonn Aug 28 '22 at 07:55
  • @Sonn huh, you can ask that question as its own question not via comments... – solujic Aug 30 '22 at 07:45
  • In my case this was the solution: `Resource resource = new ClassPathResource("path"); //In my case the file was in resource/reports. "path" = reports/image.png InputStream inputStream = resource.getInputStream();` – Alexander Ochoa Aug 31 '23 at 23:35
84

In the spring jar package, I use new ClassPathResource(filename).getFile(), which throws the exception:

cannot be resolved to absolute file path because it does not reside in the file system: jar

But using new ClassPathResource(filename).getInputStream() will solve this problem. The reason is that the configuration file in the jar does not exist in the operating system's file tree,so must use getInputStream().

Anders Metnik
  • 6,096
  • 7
  • 40
  • 79
taozi
  • 841
  • 6
  • 3
70

I know this question has already been answered. However, for those using spring boot, this link helped me - https://smarterco.de/java-load-file-classpath-spring-boot/

However, the resourceLoader.getResource("classpath:file.txt").getFile(); was causing this problem and sbk's comment:

That's it. A java.io.File represents a file on the file system, in a directory structure. The Jar is a java.io.File. But anything within that file is beyond the reach of java.io.File. As far as java is concerned, until it is uncompressed, a class in jar file is no different than a word in a word document.

helped me understand why to use getInputStream() instead. It works for me now!

Thanks!

TuGordoBello
  • 4,350
  • 9
  • 52
  • 78
jmathewt
  • 917
  • 7
  • 9
  • Hi do you know how to create a file while my spring application is in the docker container? If you can answer my question, thanks!! – Sonn Aug 28 '22 at 07:55
14

The error message is correct (if not very helpful): the file we're trying to load is not a file on the filesystem, but a chunk of bytes in a ZIP inside a ZIP.

Through experimentation (Java 11, Spring Boot 2.3.x), I found this to work without changing any config or even a wildcard:

var resource = ResourceUtils.getURL("classpath:some/resource/in/a/dependency");
new BufferedReader(
  new InputStreamReader(resource.openStream())
).lines().forEach(System.out::println);
Raphael
  • 9,779
  • 5
  • 63
  • 94
3

I was having an issue recursively loading resources in my Spring app, and found that the issue was I should be using resource.getInputStream. Here's an example showing how to recursively read in all files in config/myfiles that are json files.

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);

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;
  }
}
Brad Parks
  • 66,836
  • 64
  • 257
  • 336
  • Hi do you know how to create a file while my spring application is in the docker container? If you can answer my question, thanks!! – Sonn Aug 28 '22 at 07:57
2

I had similar problem when using Tomcat6.x and none of the advices I found was helping. At the end I deleted work folder (of Tomcat) and the problem gone.

I know it is illogical but for documentation purpose...

takacsot
  • 1,727
  • 2
  • 19
  • 30
2

For kotlin users, I solved it like this:

val url = ResourceUtils.getURL("classpath:$fileName")
val response = url.openStream().bufferedReader().readText()
GabrielBB
  • 2,479
  • 1
  • 35
  • 49
  • This worked for me. If a file is inside a folder you have to reference the folder then the file name i.e `/data/myfile.json` – Mfuon Leonard Jan 23 '23 at 08:58
1

The answer by @sbk is the way we should do it in spring-boot environment (apart from @Value("${classpath*:})), in my opinion. But in my scenario it was not working if the execute from standalone jar..may be I did something wrong.

But this can be another way of doing this,

InputStream is = this.getClass().getClassLoader().getResourceAsStream(<relative path of the resource from resource directory>);
Abhishek Chatterjee
  • 1,962
  • 2
  • 23
  • 31
  • Hi do you know how to create a file while my spring application is in the docker container? If you can answer my question, thanks!! – Sonn Aug 28 '22 at 07:57
1

I was having an issue more complex because I have more than one file with same name, one is in the main Spring Boot jar and others are in jars inside main fat jar. My solution was getting all the resources with same name and after that get the one I needed filtering by package name. To get all the files:

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

In Spring boot 1.5.22.RELEASE Jar packaging this worked for me:

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

"example.pdf" is in src/main/resources.

And then to read it as byte[]

FileCopyUtils.copyToByteArray(resource);
Its-Saab
  • 51
  • 3
  • Hi do you know how to create a file while my spring application is in the docker container? If you can answer my question, thanks!! – Sonn Aug 28 '22 at 07:58
0

I had the same issue, ended up using the much more convenient Guava Resources:

Resources.getResource("my.file")
Ahmad Abdelghany
  • 11,983
  • 5
  • 41
  • 36
0

While this is a very old thread, but I also faced the same issue while adding FCM in a Spring Boot Application.

In development, the file was getting opened and no errors but when I deployed the application to AWS Elastic beanstalk , the error of FileNotFoundException was getting thrown and FCM was not working.

So here's my solution to get it working on both development env and jar deployment production.

I have a Component class FCMService which has a method as follows:

  @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();
    }

  }

Hope this helps someone looking for a quick fix with implementing FCM.

Abdullah Umer
  • 4,234
  • 5
  • 36
  • 65
0

Can be handled like:

var serviceAccount = ClassLoader.getSystemResourceAsStream(FB_CONFIG_FILE_NAME);

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

Where FB_CONFIG_FILE_NAME is name of file in your 'resources' folder.

Oleksandr Yefymov
  • 6,081
  • 2
  • 22
  • 32