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);
Related
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.
I have explored every single solution related to this problem on Stack Overflow, and the solutions I encountered worked only in my IDE (Eclipse Mars), but when exported they all failed.
I have an image file I simply want to load from inside my exported jar.
As you can see from the project layout, Images is a folder directly inside of the project folder CooldownTrackerJava. To refer to it, I've tried a variety of methods.
//My class' name is AddItemDialog
//Method 1
String filePath = File.separator + "Images" + File.separator + "questionMark.png";
InputStream inputStream = AddItemDialog.class.getResourceAsStream(filePath);
BufferedImage img = ImageIO.read(inputStream);
I tried method 1 in a few ways. I tried it with and without the File.separator at the start of the filePath. I understand that the former refers to an absolute path, while the latter is relative. I also tried moving the Images folder inside of the directory where my class files were.
All of these failed giving me a NullPointerException when exported and tested multiple times.
//Method 2
String saveLocation = File.separator + "Images" + File.separator + "questionMark.png";
Image img = Toolkit.getDefaultToolkit().getImage(getClass().getResource(saveLocation));
As per this post I tried the above method however it still did not work.
I've tried many variations aside from the ones mentioned in this post but none of them have been successful when I exported the jar.
Do I need to add the Images folder to my build path? Should I move it to the src folder? Am I missing one obvious step? All suggestions would be highly appreciated. Also here is the layout of the exported jar file.
The solution was a two-part effort. Firstly, I moved my Images folder into the src directory as shown below. Kudos to Matthew.
I then removed File.separator and replaced it with a forward slash/. Thanks to fge.
Do not use File.separator(). Separators for resource paths on the class path is always /. If you use Windows you will therefore always get the wrong result.
I used a variant of method 1 to test it. Both methods will work though.
String saveLocation = "/Images/questionMark.png";
InputStream inputStream = this.getClass().getResourceAsStream(saveLocation);
try {
BufferedImage img = ImageIO.read(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
I'm trying to figure out how to add a lot of images into my Java program without having to add every file name in the code.
So I want to take all the .png images in the correct folder and make them into an IconImage and then place them in an ArrayList.
What I've got right now is this.
private void fillList()
{
File[] files = new File("C:/Users/marre/Google Drive/Java/lek/imageplay/src/img").listFiles();
for (File f : files)
{
if (f.isFile())
{
ImageIcon tempIcon = new ImageIcon(getClass().getResource("/img/"+f.getName()));
List.add(tempIcon);
}
}
}
This works the way I want it to regarding getting the names of all images. But the file only reads from absolute file path.
What can I use instead to get the same result, but that works within .jar file (so it reads from src/img/ and not the whole c:/ bla bla..)?
I'm not sure if this will be helpful but I had a similar issue in attempting to obtain files within a jar file. In the case this can be useful to you, my approach was to obtain the URL for the files inside the jar using the ClassLoader:
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
URL url = classLoader.getResource("src/img/");
Check inside your jar to make sure of the directory structure for the images so you can specify the correct relative path.
Keep in mind, a jar is a packaged form, so this will most likely return a zipped file (and not a simple File object). You might have to dabble in ZipFile.
ZipFile folderInsideJar = new ZipFile(new File(url.getPath());
Enumeration<? extends ZipEntry> entries = folderInsideJar.entries();
while (entries.hasMoreElements()) {
// ideally this would be your image file, zipped
ZipEntry childFile = entries.nextElement();
// ... etc ...
}
For environments such as JBoss, etc, you might also get virtual files (vfsfile/vfszip). Point being, you can determine what you are dealing with by looking at the result URL.getProtocol().
It would also appear ImageIcon has a constructor that takes a URL. You might try giving this a shot to see if it handles the "resource inside jar" case before manually attempting to retrieve the zip file.
Im trying to write a program to read a text file through args but when i run it, it always says the file can't be found even though i placed it inside the same folder as the main.java that im running.
Does anyone know the solution to my problem or a better way of reading a text file?
Do not use relative paths in java.io.File.
It will become relative to the current working directory which is dependent on the way how you run the application which in turn is not controllable from inside your application. It will only lead to portability trouble. If you run it from inside Eclipse, the path will be relative to /path/to/eclipse/workspace/projectname. If you run it from inside command console, it will be relative to currently opened folder (even though when you run the code by absolute path!). If you run it by doubleclicking the JAR, it will be relative to the root folder of the JAR. If you run it in a webserver, it will be relative to the /path/to/webserver/binaries. Etcetera.
Always use absolute paths in java.io.File, no excuses.
For best portability and less headache with absolute paths, just place the file in a path covered by the runtime classpath (or add its path to the runtime classpath). This way you can get the file by Class#getResource() or its content by Class#getResourceAsStream(). If it's in the same folder (package) as your current class, then it's already in the classpath. To access it, just do:
public MyClass() {
URL url = getClass().getResource("filename.txt");
File file = new File(url.getPath());
InputStream input = new FileInputStream(file);
// ...
}
or
public MyClass() {
InputStream input = getClass().getResourceAsStream("filename.txt");
// ...
}
Try giving an absolute path to the filename.
Also, post the code so that we can see what exactly you're trying.
When you are opening a file with a relative file name in Java (and in general) it opens it relative to the working directory.
you can find the current working directory of your process using
String workindDir = new File(".").getAbsoultePath()
Make sure you are running your program from the correct directory (or change the file name so that it will be relative to where you are running it from).
If you're using Eclipse (or a similar IDE), the problem arises from the fact that your program is run from a few directories above where the actual source is located. Try moving your file up a level or two in the project tree.
Check out this question for more detail.
The simplest solution is to create a new file, then see where the output file is. That is the correct place to put your input file into.
If you put the file and the class working with it under same package can you use this:
Class A {
void readFile (String fileName) {
Url tmp = A.class.getResource (fileName);
// Or Url tmp = this.getClass().getResource (fileName);
File tmpFile = File (tmp);
if (tmpFile.exists())
System.out.print("I found the file.")
}
}
It will help if you read about classloaders.
say I have a text file input.txt which is located on the desktop
and input.txt has the following content
i came
i saw
i left
and below is the java code for reading that text file
public class ReadInputFromTextFile {
public static void main(String[] args) throws Exception
{
File file = new File(
"/Users/viveksingh/desktop/input.txt");
BufferedReader br
= new BufferedReader(new FileReader(file));
String st;
while ((st = br.readLine()) != null)
System.out.println(st);
}
}
output on the console:
i came
i saw
i left