How to reference a resource file correctly for JAR and Debugging?
JavaEclipseJarJava Problem Overview
I have a nasty problem referencing resources when using a Maven project and Jar files...
I have all my resources in a dedicated folder /src/main/resources which is part of the build path in Eclipse. Files are referenced using
getClass().getResource("/filename.txt")
This works fine in Eclipse but fails in the Jar file - there the resources are located in a folder directly below the jar's root...
Does anyone know a 'best practice' to reference a file both in the JAR and (!) in Eclipse?
Edit: The problem is that while the resources actually are located in the JAR in a folder "resources" at the top level, the above method fails to find the file...
Java Solutions
Solution 1 - Java
Once you pack the JAR, your resource files are not files any more, but stream, so getResource
will not work!
Use getResourceAsStream
.
To get the "file" content, use https://commons.apache.org/proper/commons-io/javadocs/api-release/org/apache/commons/io/IOUtils.html:
static public String getFile(String fileName)
{
//Get file from resources folder
ClassLoader classLoader = (new A_CLASS()).getClass().getClassLoader();
InputStream stream = classLoader.getResourceAsStream(fileName);
try
{
if (stream == null)
{
throw new Exception("Cannot find file " + fileName);
}
return IOUtils.toString(stream);
}
catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
return null;
}
Solution 2 - Java
I had a similar problem. After a full day of trying every combination and debugging I tried getClass().getResourceAsStream("resources/filename.txt") and got it to work finally. Nothing else helped.
Solution 3 - Java
The contents of Maven resource folders are copied to target/classes and from there to the root of the resulting Jar file. That is the expected behaviour.
What I don't understand is what the problem is in your scenario. Referencing a Resource through getClass().getResource("/filename.txt")
starts at the root of the classpath, whether that (or an element of it) is target/classes
or the JAR's root. The only possible error I see is that you are using the wrong ClassLoader
.
Make sure that the class that uses the resource is in the same artifact (JAR) as the resource and do ThatClass.class.getResource("/path/with/slash")
or ThatClass.class.getClassLoader().getResource("path/without/slash")
.
But apart from that: if it isn't working, you are probably doing something wrong somewhere in the build process. Can you verify that the resource is in the JAR?
Solution 4 - Java
If you add the resources directory in the jar file (so it is under the /resources folder in the jar, and if /src/main is in your build path in eclipse, then you should be able to reference your file as:
getClass().getResource("/resources/filename.txt");
Which should work in both environments.
Solution 5 - Java
The problem is that within the IDE the getClass().getResource("Path"); String is not CASE SENSITIVE when accessing a file but when running from a jar it is. Check your Capitalisation on directory compared to file. It does work. Also if you try new File(getClass().getResource("Path"); the file won't be readable outside IDE.
Solution 6 - Java
Maybe this method can help for some situations.
public static File getResourceFile(String relativePath)
{
File file = null;
URL location = <Class>.class.getProtectionDomain().getCodeSource().getLocation();
String codeLocation = location.toString();
try{
if (codeLocation.endsWith(".jar"){
//Call from jar
Path path = Paths.get(location.toURI()).resolve("../classes/" + relativePath).normalize();
file = path.toFile();
}else{
//Call from IDE
file = new File(<Class>.class.getClassLoader().getResource(relativePath).getPath());
}
}catch(URISyntaxException ex){
ex.printStackTrace();
}
return file;
}
Solution 7 - Java
Just copy the file to a temporary directory.
String tempDir = System.getProperty("java.io.tmpdir");
File file = new File(tempDir.getAbsolutePath(), "filename.txt");
if (!file.exists()) {
InputStream is = (getClass().getResourceAsStream("/filename.txt"));
Files.copy(is, file.getAbsoluteFile().toPath());
}