289

I would like to read a resource from within my jar like so:

File file;
file = new File(getClass().getResource("/file.txt").toURI());
BufferedReader reader = new BufferedReader(new FileReader(file));

//Read the file

and it works fine when running it in Eclipse, but if I export it to a jar, and then run it, there is an IllegalArgumentException:

Exception in thread "Thread-2"
java.lang.IllegalArgumentException: URI is not hierarchical

and I really don't know why but with some testing I found if I change

file = new File(getClass().getResource("/file.txt").toURI());

to

file = new File(getClass().getResource("/folder/file.txt").toURI());

then it works the opposite (it works in jar but not eclipse).

I'm using Eclipse and the folder with my file is in a class folder.

NM Naufaldo
  • 300
  • 3
  • 11
Koi
  • 2,993
  • 2
  • 10
  • 5
  • If you want to read files from a directory in jar with any numbers for files, see [Stackoverflow-Link](https://stackoverflow.com/questions/26185137/spring-boot-resource-not-found-when-using-executeable-jar/39818817#39818817) – Michael Hegner Oct 02 '16 at 16:01
  • 1
    I'm not sure that the original question was involving Spring. The link in the previous comment refers to a Spring specific answer from a different question. I believe `getResourceAsStream` is still a simpler and more portable solution to the problem. – Drew MacInnis Dec 15 '16 at 03:56

15 Answers15

458

Rather than trying to address the resource as a File just ask the ClassLoader to return an InputStream for the resource instead via getResourceAsStream:

InputStream in = getClass().getResourceAsStream("/file.txt"); 
BufferedReader reader = new BufferedReader(new InputStreamReader(in));

As long as the file.txt resource is available on the classpath then this approach will work the same way regardless of whether the file.txt resource is in a classes/ directory or inside a jar.

The URI is not hierarchical occurs because the URI for a resource within a jar file is going to look something like this: file:/example.jar!/file.txt. You cannot read the entries within a jar (a zip file) like it was a plain old File.

This is explained well by the answers to:

Drew MacInnis
  • 7,249
  • 1
  • 18
  • 18
  • 3
    Thank you, this was very helpful and the code works perfectly, but I do have one problem, I need to determine whether the `InputStream` exists (like `File.exists()`) so my game can tell whether to use the default file or not. Thanks. – Koi Dec 05 '13 at 15:23
  • 1
    Oh and BTW the reason `getClass().getResource("**/folder**/file.txt")` made it work is because I had that folder in the same directory as my jar :). – Koi Dec 05 '13 at 15:33
  • 7
    `getResourceAsStream` returns null if the resource does not exist so that can be your "exists" test. – Drew MacInnis Dec 05 '13 at 19:05
  • 2
    And of course... don't forget to close the inputStream and BufferedReader – Noremac May 15 '15 at 13:38
  • 1
    I did this and put my sound file in NetBeansProjects\Test\build\classes path, but with clean and build will be cleared from this path. And by converting to exe, it no longer has access to the sound file. Where is my problem? – ParisaN Mar 12 '18 at 19:05
  • 1
    @ParisaNanderi I think your comment would be better as another question since your NetBeans setup and use of exe differ greatly from the original question. – Drew MacInnis Mar 12 '18 at 22:08
  • If you're using IntelliJ Idea, remember to right-click on your "resources" directory and "mark directory as Resources root" – ATutorMe Jul 05 '18 at 09:56
  • `the URI for a resource within a jar file is going to look something like this: file:/example.jar!/file.txt` thanks for this information, this exclamation mark in the URI was confusing me. – weibeld Jul 12 '18 at 03:43
  • Thanks this worked, completing the exact code `InputStream inputStream = this.getClass().getResourceAsStream(YourfilePath)` – Tayab Hussain Jan 14 '19 at 16:21
  • 2
    `You cannot read the entries within a jar (a zip file) like it was a plain old File.` This sucks because there are a ton of library functions that expect a file path as an input. – ptkvsk Jan 08 '20 at 16:24
  • While it is true that lots of APIs may work with File, where the class loader is involved it is trying to let you load the _resources_ associated with your application in a manner that is _independent_ of how they happen to be stored/located. https://docs.oracle.com/javase/8/docs/technotes/guides/lang/resources.html – Drew MacInnis Jan 08 '20 at 18:52
  • Just FYI: This answer is discussed on [meta](https://meta.stackoverflow.com/q/400658/5515060) – Lino Aug 27 '20 at 06:58
  • ok but, can you add the java NIO 7+ way of getting files from the jar? I need them as file objects so I can recursively find entries using the same file apis rather then having two statements for jar/zip files. – nullsector76 Jan 09 '21 at 03:28
33

To access a file in a jar you have two options:

  • Place the file in directory structure matching your package name (after extracting .jar file, it should be in the same directory as .class file), then access it using getClass().getResourceAsStream("file.txt")

  • Place the file at the root (after extracting .jar file, it should be in the root), then access it using Thread.currentThread().getContextClassLoader().getResourceAsStream("file.txt")

The first option may not work when jar is used as a plugin.

Juozas Kontvainis
  • 8,653
  • 6
  • 50
  • 65
  • Thank you very much for this great answer ... Another benefit from the second option is that it also works from the main method, because getClass() only works from an instance and not from static methods. – Dan Ortega Feb 05 '20 at 20:43
8

I had this problem before and I made fallback way for loading. Basically first way work within .jar file and second way works within eclipse or other IDE.

public class MyClass {

    public static InputStream accessFile() {
        String resource = "my-file-located-in-resources.txt";

        // this is the path within the jar file
        InputStream input = MyClass.class.getResourceAsStream("/resources/" + resource);
        if (input == null) {
            // this is how we load file within editor (eg eclipse)
            input = MyClass.class.getClassLoader().getResourceAsStream(resource);
        }

        return input;
    }
}
MForm
  • 197
  • 1
  • 3
4

Up until now (December 2017), this is the only solution I found which works both inside and outside the IDE.

Use PathMatchingResourcePatternResolver

Note: it works also in spring-boot

In this example I'm reading some files located in src/main/resources/my_folder:

try {
    // Get all the files under this inner resource folder: my_folder
    String scannedPackage = "my_folder/*";
    PathMatchingResourcePatternResolver scanner = new PathMatchingResourcePatternResolver();
    Resource[] resources = scanner.getResources(scannedPackage);

    if (resources == null || resources.length == 0)
        log.warn("Warning: could not find any resources in this scanned package: " + scannedPackage);
    else {
        for (Resource resource : resources) {
            log.info(resource.getFilename());
            // Read the file content (I used BufferedReader, but there are other solutions for that):
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(resource.getInputStream()));
            String line = null;
            while ((line = bufferedReader.readLine()) != null) {
                // ...
                // ...                      
            }
            bufferedReader.close();
        }
    }
} catch (Exception e) {
    throw new Exception("Failed to read the resources folder: " + e.getMessage(), e);
}
Naor Bar
  • 1,415
  • 14
  • 12
2

The problem is that certain third party libraries require file pathnames rather than input streams. Most of the answers don't address this issue.

In this case, one workaround is to copy the resource contents into a temporary file. The following example uses jUnit's TemporaryFolder.

    private List<String> decomposePath(String path){
        List<String> reversed = Lists.newArrayList();
        File currFile = new File(path);
        while(currFile != null){
            reversed.add(currFile.getName());
            currFile = currFile.getParentFile();
        }
        return Lists.reverse(reversed);
    }

    private String writeResourceToFile(String resourceName) throws IOException {
        ClassLoader loader = getClass().getClassLoader();
        InputStream configStream = loader.getResourceAsStream(resourceName);
        List<String> pathComponents = decomposePath(resourceName);
        folder.newFolder(pathComponents.subList(0, pathComponents.size() - 1).toArray(new String[0]));
        File tmpFile = folder.newFile(resourceName);
        Files.copy(configStream, tmpFile.toPath(), REPLACE_EXISTING);
        return tmpFile.getAbsolutePath();
    }
Navneeth
  • 333
  • 2
  • 13
  • This is glossed over a lot. Thanks for documenting it. I'm curious what other options exist without JUnit. – Alex Moore-Niemi Mar 09 '20 at 21:07
  • 3
    Since the `resourceName` should always use `/` as separator, unlike the `File` operations inside `decomposePath`, which use the system specific file separator, the method is not only unnecessarily complicated compared to `path.split("/")`, it’s even incorrect. Further, it’s not clear why you invoke `.toArray(new String[0]))` when you don’t use its result. – Holger Aug 26 '20 at 08:22
  • what is `folder`, from which library is `Lists`, what is `REPLACE_EXISTING`...? Please provide full information on your posts. – ElectRocnic Oct 01 '20 at 14:01
2

In my case I finally made it with

import java.lang.Thread;
import java.io.BufferedReader;
import java.io.InputStreamReader;

final BufferedReader in = new BufferedReader(new InputStreamReader(
      Thread.currentThread().getContextClassLoader().getResourceAsStream("file.txt"))
); // no initial slash in file.txt
user9869932
  • 4,537
  • 3
  • 42
  • 42
  • 1
    Strangely enough this was also the solution in my case with nested resources. I wonder why it seems to work with the slash in the other posted answers though. – ElectRocnic Oct 01 '20 at 19:51
  • 1
    @ElectRocnic I think it's because how paths are interpreted. "/file.txt" says, switch to the current directory and open file.txt whereas "file.txt" says, open file.txt from current location. They both lead to the same location. For me, both works in a windows system. – srishti sharma Oct 07 '20 at 05:02
  • aha, it doesn't work for me in a docker+linux environment... thanks for your statement – ElectRocnic Oct 07 '20 at 09:49
1

Make sure that you work with the correct separator. I replaced all / in a relative path with a File.separator. This worked fine in the IDE, however did not work in the build JAR.

Petterson
  • 803
  • 9
  • 18
1

I have found a fix

BufferedReader br = new BufferedReader(new InputStreamReader(Main.class.getResourceAsStream(path)));

Replace "Main" with the java class you coded it in. replace "path" with the path within the jar file.

for example, if you put State1.txt in the package com.issac.state, then type the path as "/com/issac/state/State1" if you run Linux or Mac. If you run Windows then type the path as "\com\issac\state\State1". Don't add the .txt extension to the file unless the File not found exception occurs.

0

This code works both in Eclipse and in Exported Runnable JAR

private String writeResourceToFile(String resourceName) throws IOException {
    File outFile = new File(certPath + File.separator + resourceName);

    if (outFile.isFile())
        return outFile.getAbsolutePath();
    
    InputStream resourceStream = null;
    
    // Java: In caso di JAR dentro il JAR applicativo 
    URLClassLoader urlClassLoader = (URLClassLoader)Cypher.class.getClassLoader();
    URL url = urlClassLoader.findResource(resourceName);
    if (url != null) {
        URLConnection conn = url.openConnection();
        if (conn != null) {
            resourceStream = conn.getInputStream();
        }
    }
    
    if (resourceStream != null) {
        Files.copy(resourceStream, outFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
        return outFile.getAbsolutePath();
    } else {
        System.out.println("Embedded Resource " + resourceName + " not found.");
    }
    
    return "";
}   
0

I Tried all the mentioned things here , so let me rephrase my problem ,

javax.xml.ws.Service service =
          javax.xml.ws.Service
              .create(new File("src/main/resources/wsdl/iridium/iridium.wsdl").toURI().toURL(),
                  new QName(WWW_IRIDIUM_COM, IWS));

This wsdl file is in the java project I will be using a jar , so when I import this jar to a different project , I am getting file not found exception since the project is trying to look into its resources folder to start the spring boot app .

what changes I need to make to make this project start with this jar as dependency .

divyanayan awasthi
  • 652
  • 1
  • 6
  • 23
  • I used this and it worked ..just writing it for future reference for anyone to use - javax.xml.ws.Service service = javax.xml.ws.Service .create(IridiumClientService.class.getClassLoader().getResource("iridium.wsdl"), new QName(WWW_IRIDIUM_COM, IWS)); – divyanayan awasthi Mar 21 '21 at 20:57
  • Trick here is to make sure your are looking int the correct folder . check target folder to identify where your file is – divyanayan awasthi Mar 22 '21 at 16:13
-1

You can use class loader which will read from classpath as ROOT path (without "/" in the beginning)

InputStream in = getClass().getClassLoader().getResourceAsStream("file.txt"); 
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
sendon1982
  • 7,088
  • 42
  • 36
-1

For some reason classLoader.getResource() always returned null when I deployed the web application to WildFly 14. getting classLoader from getClass().getClassLoader() or Thread.currentThread().getContextClassLoader() returns null.

getClass().getClassLoader() API doc says,

"Returns the class loader for the class. Some implementations may use null to represent the bootstrap class loader. This method will return null in such implementations if this class was loaded by the bootstrap class loader."

may be if you are using WildFly and yours web application try this

request.getServletContext().getResource() returned the resource url. Here request is an object of ServletRequest.

Ram C
  • 173
  • 2
  • 12
-2

If you are using spring, then you can use the the following method to read file from src/main/resources:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import org.springframework.core.io.ClassPathResource;

  public String readFileToString(String path) throws IOException {

    StringBuilder resultBuilder = new StringBuilder("");
    ClassPathResource resource = new ClassPathResource(path);

    try (
        InputStream inputStream = resource.getInputStream();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream))) {

      String line;

      while ((line = bufferedReader.readLine()) != null) {
        resultBuilder.append(line);
      }

    }

    return resultBuilder.toString();
  }
Tristan
  • 7,774
  • 6
  • 41
  • 77
Sujan M.
  • 31
  • 2
  • 1
    Welcome to SO. This does not provide an answer to the question. Once you have sufficient [reputation](http://stackoverflow.com/help/whats-reputation) you will be able to [comment](http://stackoverflow.com/help/privileges/comment) on any post. Also check this [what can I do instead](https://meta.stackexchange.com/questions/214173/why-do-i-need-50-reputation-to-comment-what-can-i-do-instead). – thewaywewere May 03 '17 at 15:36
  • This will remove the line breaks in the file ! – elad.chen Mar 03 '19 at 09:44
-2

Below code works with Spring boot(kotlin):

val authReader = InputStreamReader(javaClass.getResourceAsStream("/file1.json"))
oguz ismail
  • 34,491
  • 11
  • 33
  • 56
-3

If you wanna read as a file, I believe there still is a similar solution:

    ClassLoader classLoader = getClass().getClassLoader();
    File file = new File(classLoader.getResource("file/test.xml").getFile());
pablo.vix
  • 1,799
  • 2
  • 13
  • 12
  • 5
    URL.getFile() *does not* convert a URL to a file name. It returns the portion of the URL after the host, with all percent-encodings intact, so if the path contains any non-ASCII characters or any ASCII characters not allowed in URLs (including spaces), the result will not be an existing file name, even if the URL is a `file:` URL. – VGR Mar 05 '16 at 18:07
  • 51
    this does not work inside once the program is build to a jar – Akshay Kasar Sep 20 '17 at 12:42
  • 1
    Doesn't work from jar unless you convert to string and save it locally first. – smoosh911 Aug 09 '18 at 16:55