Jar not finding image - java

I have a maven project with spring-boot. I have a png image in my-module/src/resources/images/image.png.
I make mvn clean package and a Jar is generated, ok, now I run the Jar as java -jar my_jar.jar but I get this error java.io.FileNotFoundException: /Users/jose/projects/my-project/classpath:images/image.png
In code I have:
private static final String IMAGE_PATH = "classpath:images/image.png";
and
Image image = PngImage.getImage(IMAGE_PATH);
Seems that it does not get :classpath keyword as it should, but as a literal.
When I unzip the Jar file the image is located in /BOOT-INF/classes/images/image.png
The idea is not to change the code if possible but the way of execute or generate the Jar.
All suggestions are welcome though.
Thank you

I'm not sure what PngImage class you are using. But if the getImage() method accepts an InputStream, you can use:
#Value("classpath:images/image.png")
private Resource resource;
And then:
InputStream inputStream = resource.getInputStream();
The following also should work:
ClassPathResource resource = new ClassPathResource("images/image.png");
InputStream inputStream = resource.getInputStream();

Related

Using file from within JAR, while runnig app from jar

I am trying to use file, when running application as JAR.
When I run application through Intelij, everything is fine. However when I try to run it via jar, I cannot access the file.
I tried to read few topics containing similar matter, but non of them help
(like Reading a resource file from within jar or How do I read a resource file from a Java jar file?
)
Here is my target tree, and resources:
When I use
String path = String
.join("", "classpath:static\assets\config\", fileName);
File file = ResourceUtils.getFile(path);
InputStream targetStream = new FileInputStream(file)
During intelij run, everything works.
In the case of jar, I tried:
String path = String
.join("", "static\assets\config\", fileName).replace("\\","/")).toExternalForm();
String path2 = String
.join("", "static\assets\config\", fileName).replace("\\","/")).getFile();
String path3 = String
.join("", "static\assets\config\", fileName).replace("\\","/")).getPath();
and many other. They result in correct path, for example:
file:/D:/Projects/myProject/target/classes/static/assets/config/fileName (in case of toExternalForm)
/D:/Projects/myProject/target/classes/static/assets/config/fileName (in case of getFile)
However all of them results in null InputStream, when I try:
InputStream in = getClass().getResourceAsStream(everyPath);
I get an error:
java.io.FileNotFoundException: D:\Projects\myProject\target\project-app-1.0.jar\BOOT-INF\classes\static\assets\config\fileName (The system cannot find the path specified)
When the path in the project-app-1.0.jar when I open it by 7zip is exactly:
D:\Projects\myProject\target\project-app-1.0.jar\BOOT-INF\classes\static\assets\config\fileName
This is how my resource handler looks like:
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
"classpath:/resources/", "classpath:/static/"};
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations(
CLASSPATH_RESOURCE_LOCATIONS);
}
forget about "files" when you want to use something inside your jar, its just a simple "Resource" that your have to use with getResource.
If you use a standard packaging system, everything inside "resources" folder are put at the root of your JAR, so if you want to read your "foo.txt" file inside "static\assets\config\" folder you have to do use method:
InputStream in = ClassLoader.getSystemResourceAsStream("static/assets/config/foo.txt");

Executable JAR built with maven unable to find resources when loaded from static method

So I have a small resource loader for stuff that I need. The jar packages the resources but when I build the maven project, and all the dependencies work and resources folder is marked as resources, my images and scripts wont load.
Here is the code I am using:
...
public class ResourceLoader
{
public static ImageIcon getImageIconResource(String fileName) {
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
File file = new File(classLoader.getResource("img/" + fileName).getFile());
return new ImageIcon(file.getPath());
}
public static File getScriptResource(String fileName) {
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
return new File(classLoader.getResource("scripts/" + fileName).getFile());
}
}
The problem is not with maven itself. When you call
File.getPath
on File from the resource it points to a File, that is actually inside of your application archive. For most applications, this pose a problem because you cannot read file without extracting the archive. To correctly use resource file, you have to work with File, or you can call
ClassLoader.getResourcesAsStream
To adress ImageIcon
public static ImageIcon getImageIconResource(String fileName) {
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
InputStream is = classLoader.getResourceAsStream("img/" + fileName);
Image image = ImageIO.read(is);
return new ImageIcon(image);
}
As for your getScriptResource method geting File object should work. But that depends, on how you will later use it. As I think you will need to read it anyway at some point I suggest using input stream as well.
public static InpoutStream getScriptResource(String fileName) {
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
InputStream is = classLoader.getResourceAsStream("scripts/" + fileName);
return is;
}
Then, you can read the InputStream using many options that suits your need. For example, you can take a look at Apache Commons' IoUtils or handle it using ReaderApi
EDIT:
Because you have clarified how you will use your file I can see where is the problem with your scripts. You are starting another process outside of your application. In the first CLI param of python3, you are providing path to the file. As I wrote earlier - this is the problem, because python3 cannot read file inside of .jar file. First of all, I would have questioned your architecture. Do you really need to have script inside of .jar?
Anyway, one possible workaround may be storing contents of a script File in temporaryFile.
File tempFile = File.createTempFile("prefix-", "-suffix");
// e.g.: File tempFile = File.createTempFile("MyAppName-", ".tmp");
tempFile.deleteOnExit();
//get your script and prepare OutputStream to tempFile
// Try with resources, because you want to close your streams
try (InputStream is = ResourceLoader.getScriptResource(scriptName);
FileOutputStream out = new FileOutputStream(tempFile)) {
//NOTE: You can use any method to copy InputStream to OutputStream.
//Here I have used Apache IO Utils
IOUtils.copy(is, out);
}
boolean success = executePythonScriptWithArgs(tempFile, args);

Deploying images used in Java Application with the built jar file

After using images for example on a Button, when I build the application creating the .jar file and execute only the file, the images are not there but would only show if I copy the images folder in the same directory as the jar file. Why is that and how can I resolve this if possible?
I am currently using the following code to set the icon/image:
JButton btn = new JButton("Text", "img/icon.png");
The fact that you can use the images when they are stored outside the jar, suggests that you are doing something of the kind:
File image = new File("directory/image.jpg");
InputStream is = new FileInputStream(image);
This reads a file from a directory on the file system, not from the classpath. Now, if you have packaged your image in a "directory" inside your Jar, you must load the image from the classpath.
InputStream is = getClass().getResourceAsStream("/directory/image.jpg");
(note the slash in the path)
Or
InputStream is = getClass().getClassLoader().getResourceAsStream("directory/image.jpg");
(note the absence of the slash in the path)
Your example, as it is now, should not compile. (The second argument of your JButton construtor is an Icon, not a String, java 8). So when you were getting the image from the file system, you were probably doing something else.
With your example, you need to read an image from the inputstream and convert it to an Icon:
try (InputStream is = getClass.getClassLoader().getResourcesAsStream("directory/image.jpg")) {
BufferedImage image = ImageIO.read(is);
return new JButton("Text", new ImageIcon(image));
} catch (IOException exc) {
throw new RuntimeException(exc);
}
That should use the image that is located in "directory" inside your jar. Of course, you need to include the image within your jar, or you will get a NullPointerException on the inputstream is.
I think you need to understand the
ClassLoader:
A typical strategy is to transform the name into a file name and then
read a "class file" of that name from a file system.
So with this you will be able to lead Resources of your project with getResource
public URL getResource(String name)
Finds the resource with the given name. A resource is some data
(images, audio, text, etc) that can be accessed by class code in a way
that is independent of the location of the code.

java.lang.NullPointerException while getting a filepath in Java (executing through a jar file)

I am getting an NPE at the point of getting path of a File (an sh file in assets folder).
I have tried to read about NPE i detail from the following thread, but this actually could not solve my problem.
What is a NullPointerException, and how do I fix it?
Following is my code snippet:
File absPathofBash;
url = ClassLoader.class.getResource("assets/forbackingup.sh");
absPathofBash = new File(url.getPath());
Later I'm using it in a ProcessBuilder, as
ProcessBuilder pb = new ProcessBuilder(url.getPath(), param2, param3)
I've also tried getting the absolute path directly, like
absPathofBash = new File("assets/forbackingup.sh").getAbsolutePath();
Using the latter way, I am able to process it, but if I create a jar then the file cannot be found. (although the Jar contains the file within the respective folder assets)
I would be thankful if anyone can help me on that.
Once you have packaged your code as a jar, you can not load files that are inside the jar using file path, instead they are class resources and you have to use this to load:
this.getClass().getClassLoader().getResource("assets/forbackingup.sh");
This way you load assets/forbackingup.sh as an absolute path inside your jar. you also can use this.getClass().getResource() but this way the path must be relative to this class path inside jar.
getResource method gives you an URL, if you want to get directly an InputStream you can use getResourceAsStream
Hope it helps!
Since the file itself is in the jar file, you could try using:
InputStream is = this.getClass().getClassLoader().getResourceAsStream(fileNameFromJar);
In case of jar file , classloader will return URL different than that of when the target file is not embedded inside jar. Refer to answer on link which should help u :
How to use ClassLoader.getResources() in jar file
I got it done by creating a temp file. Though it's not difficult, yet I'm posting the code patch here:
InputStream stream = MyClass.class.getClassLoader().
getResourceAsStream("assets/forbackingup.sh");
File temp = File.createTempFile("forbackingup", ".sh");
OutputStream outputStream =
new FileOutputStream(temp);
int read = 0;
byte[] bytes = new byte[1024];
while ((read = stream.read(bytes)) != -1) {
outputStream.write(bytes, 0, read);
outputStream.close();
}
Now, we have this temp file here which we can pipe to the ProcessBuilder like,
String _filePath=temp.getPath();
ProcessBuilder pb = new ProcessBuilder(url.getPath(), param2, param3)
Thank you everyone for your considerations.
You can use Path class like :
Path path = Paths.get("data/test-write.txt");
if(!Files.exists(path)){
// can handle null pointer exception
}

Read a source file for a custom ant task

I want to open a source file for my custom ant task. I thought this would be easy, because surely the class loader get resource would immediately find the source file for me. Wrong!
Here's my code:
//build the name of the template
StringBuilder sb = new StringBuilder(VersionTemplate.class.getName());
sb.append(".java");
String templateName = sb.toString();
//find the template
InputStream inputStream = VersionTemplate.class.getResourceAsStream(templateName);
inputStream is always null.
Any ideas?
Try:
templateName = templateName.replaceAll(".","/");
InputStream inputStream = VersionTemplate.class.getClassLoader().
getResourceAsStream(templateName);
Please make sure ".java" file is present in the same package as compile ".class" files.
It occurred to me after the fact that getResource() looks on the class path, which is not where the source code is. I ended up simply just coding a relative path to the code.
Thanks for the help.

Categories

Resources