I have created a program in Java that I want packaged into an executable jar file. I want this program to take images from the jar file and display them. I created an abstract class with a method to take a String filename and return an Image object. However, when I try to run this method, it fails and produces an "IOException: Stream closed" error.
I can't find anything on why the stream is closed. I don't have any other input streams in my program, as far as I know. Using the method in a new main with nothing but a JFrame set-up still produces the same error.
Whether I call the image file only by its name (i.e. "example.png") or use its relative path (i.e. "/src/icons/example.png"), OR use its absolute path (i.e. "C:/Users/My_Name/Desktop/EXAMPLE/src/icons/example.png") I receive the same stream closed error.
public static Image importImage(String fileName) throws IOException {
Image img = null;
byte[] data = new byte[10000];
BufferedInputStream bis = new BufferedInputStream( Thread.currentThread().getClass().getResourceAsStream(fileName));
int byteRead = bis.read(data, 0, 10000);
img = Toolkit.getDefaultToolkit().createImage(data);
return img;
}
I expect the program to accept the name of the image file in question, and return an Image object. The image file is on the project's classpath, and should be visible.
Okay. So as it turns out, a method like this has two requirements: One, you have to call 'thisClassName.class.getResourceAsStream(fileName).' Exactly like that. You also need to have your fileName start with '/' or it will completely not work. But, as long as the resources you are looking for are included in your program's classpath, it should work from there.
Related
I'm using a custom method to get pictures from the resources/ folder. The hardcoded path works well when programming during production (src/main/resources/). However when delivering, I would need to make this path relative to the .jar root. So I made this.
public static Image getImageFromFile(String file)
{
Image image = null;
try
{
String path = FileUtils.class.getClassLoader().getResource(file).toExternalForm();
System.out.println(path);
File pathToFile = new File(path);
image = ImageIO.read(pathToFile);
}
catch (IOException ex) {ex.printStackTrace();}
return image;
}
file:/C:/Users/Hugo/Desktop/Hugo/Java%20Workspace/ClashBot/bin/main/icons/level-label.png
javax.imageio.IIOException: Can't read input file!
at javax.imageio.ImageIO.read(Unknown Source)
at com.lycoon.clashbot.utils.FileUtils.getImageFromFile(FileUtils.java:55)
The printed path is valid and points to the corresponding picture. However, the program raises an IOException.
Why can't it find the file?
You're jumping through way too many hoops. It's quite simple:
FileUtils.class.getResource("path.png");
// -OR-
try (var in = FileUtils.class.getResourceAsStream("path.png")) {
// in is an inputstream.
}
is all you need. Note that this means the path.png file is searched for in the exact same place (and even same 'subdir') as where FileUtils lives. So if you have, say, a file on C:\Projects\Hugo\MyApp\myapp.jar, and if you were to unzip that, inside you'd find com/foo/pkg/FileUtils.class, then the string path.png would look in that jar, and for com/foo/pkg/path.png. In other words, AnyClass.class.getResource("AnyClass.class") will let a class find its own class file. If you want to go from the 'root' of the jar, add a slash, i.e. FileUtils.class.getResource("/path.png") looks in the same jar, and for /path.png inside that jar.
getResource returns a URL. getResourceAsStream returns a stream (which you need to close; use try-with-resources as I did). Just about every resource-using API out there will take one of these two as input. For example, ImageIO does so; it even takes a URL so you can use either one:
var image = ImageIO.read(FileUtils.class.getResource("imgName + ".png"));
Yes. It's a one-liner. This will load images straight from within a jar file!
You could try to use a slightly different call like this:
java.net.URL fileUrl = Thread.currentThread().getContextClassLoader().getResource(file);
String filePath = URLDecoder.decode(fileUrl.getPath(), "UTF-8");
image = ImageIO.read(filePath);
I am struggling while trying to read the bytes of a PNG image bundled with the resources of a JAR. The file is located in the src/main/resources directory.
Here is my code so far:
byte[] bytes = {};
final InputStream defaultImageStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("/defaultLogo.png");
new DataInputStream(defaultImageStream).readFully(bytes);
The code is executed on a Wildfly 12 server, located in a JAR included in the EAR as an EJB.
It seems than instead of retrieving the resource I asked for, getResourceAsStream returns the enclosing jar:
How can I get that image?
Additional info:
I tried both with an exploded and non-epxloded JAR in the EAR. Same results.
The path to the resource seems correct. Prefixing it by "/resources" ends in the method returning NULL.
I tried using the Class' classloader instead of the thread context's one. Same results.
I envisioned going through all the entries of the enclosed JAR myself, but this both seems overkill and difficult: since I have a JarInputStream and no JarFile, how would I read the data corresponding to an entry?
I think your code is working as intended. Looking at the DataInputStream instance isn't going to tell you much. Look at the content, I think it is the image you want.
You're thinking correctly, the JarInputStream would server the purpose for you.
Your code should be something like below--
try {
JarInputStream jarIS = new JarInputStream(new FileInputStream(
"jarfilePath"));
JarEntry entry = null;
while ((entry = jarIS.getNextJarEntry()) != null) {
String name = entry.getName();
if (name.endsWith("defaultLogo.png")) {
System.out.println( "You got the PNG File"+entry.getAttributes().toString() );
//Now handle your stream as per your requirement.
}
}
} catch (Exception e) {
}
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.
Currently, I am using ImageIO.write() in order to write to file. However, this method opens up a Java App on my computer, which then forcefully aborts the Bootstrap process if closed, thereby killing the 'server'. I'm testing locally, using IntelliJ, and the termination of the Bootstrap process means that we are unable to test the functionality without rebooting the server.
My method is below. It runs on an API call from our front-end.
/**
* Saves image to database, assuming that the input is not null or empty.
* #param filename name of file.
* #param fileext extension of file.
* #param uri uri in string form.
*/
public static void saveImageToDisk(String filename, String fileext, String uri) {
try {
String[] components = uri.split(",");
String img64 = components[1];
byte[] decodedBytes = DatatypeConverter.parseBase64Binary(img64);
BufferedImage bfi = ImageIO.read(new ByteArrayInputStream(decodedBytes));
File outputfile = new File(IMAGESTORAGEFOLDER + filename + "." + fileext);
ImageIO.write(bfi, fileext, outputfile);
bfi.flush();
} catch(Exception e) {
e.printStackTrace();
}
}
My question is as follows: How can I save an image (from Raw Data) to file without the server aborting? If my code can be adapted with minimal rewrite, what other improvements can I make to robustify my existing code? I would like a solution with no external dependencies (relying entirely on standard Java libraries).
I am on MacOSX, running IntelliJ IDEA CE. Our server runs with Spark and uses Maven.
Thank you very much.
ImageIO.write() [...] method opens up a Java App on my computer
The issue here is that when you use the ImageIO class, it will also initialize the AWT because of some dependencies in the Java2D class hierarchy. This causes the Java launcher on OS X to also open up an icon in the dock and some other things, and I believe this is what you experience. There's really no new Java application being launched.
You can easily avoid this by passing a system property to the Java launcher at startup, telling it to run in "headless" mode. This is usually appropriate for a server process. Pass the following on the command line (or in the IntelliJ launch dialog):
-Djava.awt.headless=true
Read more about headless mode from Oracle's pages. Headless mode is the cross-platform way of doing this. There's also an OS X/MacOS specific way to hide the icon from the dock (-Dapple.awt.UIElement=true, but I don't recommend that here.
However, for your use case it's better to avoid the usage of ImageIO altogether. It's easier, more compatible, faster, and uses less memory as a bonus. Simply write the Base64 decoded bytes directly to disk. There's no need to treat a file containing an image differently from any other file in this case.
You can rewrite your method as follows:
public static void saveImageToDisk(String filename, String fileext, String uri) {
try {
String[] components = uri.split(",");
String img64 = components[1];
byte[] decodedBytes = DatatypeConverter.parseBase64Binary(img64);
File outputfile = new File(IMAGESTORAGEFOLDER, filename + "." + fileext);
Paths.write(outputFile.toPath(), decodedBytes);
} catch(Exception e) {
// You really shouldn't swallow this exception, but I'll leave that to you...
e.printStackTrace();
}
}
After running multiple users at the same time, running the process multiple times, etc, it seems to just be an artifact of either Java's ImageIO or IntelliJ. As long as the new process is not closed, Bootstrap continues to run properly, even if multiple browsers try to upload images, etc.
I've an image in my .jar (program.jar/resources/img/image.gif) and I would want to invoke a methot at the the beggining of my main to copy them to "C:\folder". It should check if the file "C:\folder\image.gif" exist, and, if not, copy it.
I've researched about this topic, but I can't find a way to do it.
This is what I have, and it works... but it creates an empty file (image.gif) whit 0 bytes. It creates a file, not the file from my .jar.
Code:
//MyResources.saveResource("/resources/img/image.gif", "C:\folder", "image.gif");
public static void saveResource(String fromFile, String toFolder, String toFile){
InputStream stream = MyResources.class.getResourceAsStream(fromFile);
if (stream == null) {
//send your exception or warning
}
OutputStream resStreamOut;
int readBytes;
byte[] buffer = new byte [4096];
try {
resStreamOut = new FileOutputStream(new File (toFolder + File.separator + toFile));
while ((readBytes = stream.read(buffer)) != -1){ //LINE 80
resStreamOut.write (buffer, 0, readBytes);
}
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
I also get an error (a NullPointerException that explains why the files are empty, but I don't know how o solve):
Exception in thread "main" java.lang.NullPointerException at
com.github.CrafterPGSV.Chess.Library.MyResources.saveResource(MyResources.java:80)
at com.github.CrafterPGSV.Chess.Library.MyResources.createFolders(MyResources.java:61)
at com.github.CrafterPGSV.Chess.Chess.main(Chess.java:10)
My main calls "MyResources.createFolders();" which create the folder if it doesn't exist. At the end of "createFolders", it calls "saveResources(...)" to save the image.
Don't know why the other answer got delete, but you need resStreamOut.close() after you finish writing.
You need to implement the case when stream == null (throw exception presumably).
Also, never catch and swallow errors: printStackTrace() is not error handling, it is error ignoring.
The error was the first line of "saveResource()":
InputStream stream = this.getClass().getResourceAsStream(fromFile);
Here, "stream" was always null. Here is the way to solve this (using Eclipse):
Create a new package in src (let's call it resources).
Create a new folder in your package (let's call it myPictures)
Create more folders if you want. I'll create a new folder inside myPictures and I'll call it partyPictures
Choose one of your folders and put the images (or whatever) inside. I'll have an image called imageName.jpg inside partyPictures
Then, go to the "Navigator", find your Proyect, and you will see that there is a folder called bin. Inside, you should see your packages organised in folders.
Look for the folder inside bin that has the name of the package you created before (I'll have a folder called resources). Inside the folder you'll see a tree-view of the folders inside the package (the ones you created before).
Look for your files. (I'll look for resources/myPictures/partyPictures/imageName.jpg)
If it doesn't exist, InputStream stream = this.getClass().getResourceAsStream(fromFile); will always null. To fix this, copy the image (or whatever file) to the folder.
Then use
String fromFile = "/resources/myPictures/partyPictures/imageName.jpg";
InputStream stream = this.getClass().getResourceAsStream(fromFile);
Change the String fromFile to your file's path, and always start with a /.
Your code will look like this:
String fromFile = "/packageName/ folderName/ fileName.extension";
InputStream stream = this.getClass().getResourceAsStream(fromFile);
That should be it!