Reading a resource file from within jar

JavaFileJarResourcesEmbedded Resource

Java Problem Overview


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.

Java Solutions


Solution 1 - Java

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

try (InputStream in = getClass().getResourceAsStream("/file.txt");
    BufferedReader reader = new BufferedReader(new InputStreamReader(in))) {
    // Use resource
}

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:

Solution 2 - Java

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.

Solution 3 - Java

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

Solution 4 - Java

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

Solution 5 - Java

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

Solution 6 - Java

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

Solution 7 - Java

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.

Solution 8 - Java

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.

Solution 9 - Java

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 "";
}	

Solution 10 - Java

finally i solved errors:

String input_path = "resources\\file.txt";
		
		input_path = input_path.replace("\\", "/");  // doesn't work with back slash
		
		URL file_url = getClass().getClassLoader().getResource(input_path);
		String file_path = new URI(file_url.toString().replace(" ","%20")).getSchemeSpecificPart();
		InputStream file_inputStream = file_url.openStream();

Solution 11 - Java

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

Solution 12 - Java

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.

Solution 13 - Java

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

Solution 14 - Java

Below code works with Spring boot(kotlin):

val authReader = InputStreamReader(javaClass.getResourceAsStream("/file1.json"))

Solution 15 - Java

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionKoiView Question on Stackoverflow
Solution 1 - JavaDrew MacInnisView Answer on Stackoverflow
Solution 2 - JavaJuozas KontvainisView Answer on Stackoverflow
Solution 3 - JavaMFormView Answer on Stackoverflow
Solution 4 - JavaNaor BarView Answer on Stackoverflow
Solution 5 - Javauser9869932View Answer on Stackoverflow
Solution 6 - JavaNavneethView Answer on Stackoverflow
Solution 7 - JavaPettersonView Answer on Stackoverflow
Solution 8 - Javauser13072113View Answer on Stackoverflow
Solution 9 - JavaValentino RicciView Answer on Stackoverflow
Solution 10 - JavaMahdiView Answer on Stackoverflow
Solution 11 - Javasendon1982View Answer on Stackoverflow
Solution 12 - JavaRam CView Answer on Stackoverflow
Solution 13 - JavaSujan M.View Answer on Stackoverflow
Solution 14 - JavaBikash DasView Answer on Stackoverflow
Solution 15 - Javapablo.vixView Answer on Stackoverflow