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.
getResourceAsStream
is still a simpler and more portable solution to the problem.
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:
How do I read a resource file from a Java jar file?
Java Jar file: use resource errors: URI is not hierarchical
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.
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;
}
}
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);
}
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
getClass().getClassLoader().getResourceAsStream()
worked for me
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();
}
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.
folder
, from which library is Lists
, what is REPLACE_EXISTING
...? Please provide full information on your posts.
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.
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.
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 "";
}
certPath
here?
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();
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));
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.
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();
}
Below code works with Spring boot(kotlin):
val authReader = InputStreamReader(javaClass.getResourceAsStream("/file1.json"))
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());
file:
URL.
Success story sharing
InputStream
exists (likeFile.exists()
) so my game can tell whether to use the default file or not. Thanks.getClass().getResource("**/folder**/file.txt")
made it work is because I had that folder in the same directory as my jar :).getResourceAsStream
returns null if the resource does not exist so that can be your "exists" test.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.