Reading Files Within a Jar(ZipInputStream, etc.) [duplicate] - java

This question already has answers here:
How do I read a resource file from a Java jar file?
(9 answers)
Closed 9 years ago.
I'm writing a game in java and all is working more or less well, as you would expect when coding, but one issue I have come across is reading the image and sound files into the game. I wrote a method that works really well within Eclipse, but as soon as I try to test it in a runnable jar (exported using Eclipse) my game gets stuck on the loading screen because it can't read the files. I realized the problem there being that you cannot create File objects within a Jar, you have to use streams. This could work, but the images/sounds are located within folders and I'm not very sure how you can read files within a folder with streams(i.e not knowing the amount of files). Because of this I have tried to rewrite the method using the ZipInputStream (I found this as recommended when looking for solutions prior to posting this). This is what the method looks like now:
imageMap = new HashMap<String, BufferedImage>();
String imagebase = "/images";
CodeSource src = PlatformerCanvas.class.getProtectionDomain().getCodeSource();
if(src != null)
{
URL jar = src.getLocation();
try
{
ZipArchiveInputStream zip = new ZipArchiveInputStream(jar.openStream());
ZipArchiveEntry ze = null;
while((ze = zip.getNextZipEntry()) != null)
{
System.out.println(ze.getName());
if(ze.getName().startsWith(imagebase) && ze.getName().endsWith(".png"))
{
loading++;
imageMap.put(ze.getName().replaceAll(imagebase + "/", ""), ImageIO.read(this.getClass().getResource(ze.getName())));
}
}
zip.close();
System.out.println("Finished Loading Images");
}
catch (IOException e)
{
e.printStackTrace();
}
}
However, this completely skips over the while loop because getNextZipEntry return null. I tried using the base Zip archive libraries, but someone recommended the Apache Commons libraries, but neither worked. My Jar setup is this:
Jar
/game/gameClasses
/images/imageFiles
/sounds/soundFiles
/whateverOtherFoldersEclipseCreated
So my question is this: Why isn't this current method working and how do I fix it? Or, how can I load the images/sounds in a different way that works?
I have looked at many other questions and tried many different things, but none worked for me.

Or, how can I load the images/sounds in a different way that works?
One common technique is to include a list of resources in a known location in the Jar. Read the list, then iterate the lines (presuming one line per resource) & read the paths that way.

Related

JAR file not picking up images [duplicate]

This question already has answers here:
Java, display chm file loaded from resources in jar
(1 answer)
How do I determine the correct path for FXML files, CSS files, Images, and other resources needed by my JavaFX Application?
(1 answer)
Closed 1 year ago.
So I have created a java application in eclipse and I use javafx, and then exported it in a runnable jar file. However, when I run my .jar file it gives the error of not finding my images. It works perfectly fine when I run inside eclipse.
My file structure:
In UserPane I have a function that takes the image name as "title" and returns the imageview:
InputStream is=null;
try {
is=new FileInputStream("./Images/"+title+".jpg");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
Image img=new Image(is);
ImageView imgview=new ImageView();
imgview.setImage(img);
imgview.setFitHeight(300);
imgview.setFitWidth(300);
return imgview;
I have tried some solutions, but it won't run in eclipse. I need it to run in eclipse and from the .jar.
I need it to be set in the imageview please.
Update: I did not use any of the suggestions mentioned because none of them were helpful, none of them worked. What worked for me is moving my .jar in the same folder where my images folder is, while still keeping the above code.
Thank you
FileInputStream is called that because it operates on files. In java, a file means exactly what it says: A file is a thing on a disk someplace.
An entry in a jar file is NOT ITSELF a file!
Therefore, when you write new FileInputStream? You lost the game. That can never work.
What you're looking for is a thing that lets you ask the JVM to give you resources from the same place the JVM is loading your class files.
Fortunately, that exists!
URL url = UserPane.class.getResource("/Images/" + title + ".png");
This gets you a url object, which you can pass to e.g. ImageIcon.
If you want to instead read it directly:
try (InputStream in = UserPane.class.getResourceAsStream("/Images/" + title + ".png")) {
// use 'in' here. It's `null` if the resource doesn't exist.
}
For this specific use case, you're looking for the URL variant. You don't need the InputStream at all.

Java: Can't figure out how to use class.getClassLoader().getResourceAsStream() so it works when i build artifacts

I have been tasked with developing a discord bot for a friend of mine and need to load some strings from a file. Now, to make this work when i export the project into a jar, someone told me to use class.getClassLoader().getResourceAsStream()
After fiddling around for 2 hours, reading documentation and asking again, i got it to work in intelliJ. It still does not work when i build the project though.
Project Structure
My Code:
static final ArrayList<String> QUOTES = new ArrayList<>();
public static void loadArray() {
try{
//File fin = new File("src/main/java/com/github/MarvelousAdain/Quotes");
//FileInputStream fis = new FileInputStream(fin);
System.out.println("Called loadArray Method");
BufferedReader br = new BufferedReader(new InputStreamReader(Utilities.class.getClassLoader().getResourceAsStream("Quotes")));
String line;
while ((line = br.readLine()) != null) {
QUOTES.add(line);
}
System.out.println("Quotes loaded, no Problem.");
br.close();
}catch(IOException e){e.printStackTrace();}
}
If i try running my code in the jar, this throws a NullPointerException.
Stacktrace:
Exception in thread "main" java.lang.NullPointerException
at java.io.Reader.<init>(Unknown Source)
at java.io.InputStreamReader.<init>(Unknown Source)
at com.github.MarvelousAdain.Utilities.loadArray(Utilities.java:31)
at com.github.MarvelousAdain.Main.main(Main.java:18)
Help would be greatly appreciated.
Is the tool that creates the jar including the resource?
You can check with
jar tf myjar.jar
Or indeed, your favourite zip tool.
Peculiarly getResourceAsStream() returns null instead of throwing some kind of IOException like a normal API would.
Also note that ClassLoader.getResourceAsStream will take the name relative to the classpath, but Class.getResourceAsStream will modify the path using the name of the package of the specified class (i.e. in the same "directory" as the .class file).
So, this might be more of a work around, but it was the only way i got it to work right now. If someone has a better idea, which i'm sure someone has, then i'd still like to hear it.
I built the jar, opened it with winrar and put the 3 files that i want to read in there. It worked. If i put them in the same place within the IDE, it did not. I have no idea how/why this is the case, but for now this works for me. I'll update this if i find something better!

Trying to create a new file throws FileNotFoundException but file exists in the same package [duplicate]

This question already has answers here:
java.io.FileNotFoundException: the system cannot find the file specified
(8 answers)
Closed 4 years ago.
I have a csv file in the same path as everything else. Now, when I try to create a File object:
public void getMenu() {
File fileMenu = new File("FastFoodMenu.csv");
try {
Scanner inputStream = new Scanner(fileMenu);
while (inputStream.hasNext()) {
String data = inputStream.next();
System.out.println(data);
}
} catch (FileNotFoundException ex) {
Logger.getLogger(FileHandler.class.getName()).log(Level.SEVERE, null, ex);
}
}
it throws a FileNotFoundException.
the absolute path to all files in the project is:
C:\Users\kenyo\Documents\NetBeansProjects\OrderFastFood\src\fastfoodorderingsystem
I also checked the name a couple of times. fileMenu.exists() returns false.
First, in your root/working directory (in your case it's the folder containing your project), create a folder called 'menus', here you can store all your menus (so you can play around with multi-file input).
Second, move your FastFoodMenu.csv file to that menus folder.
The FastFoodMenu.csv relative path should now look like this: OrderFastFood\menus\FastFoodMenu.csv.
Third, get your working directory from the System properties. This is the folder in which your program is working in. Then, get a reference (File object) to the menus folder.
Lastly, get a reference to the file in question inside the menu folder. When you get to multi-file reading (and at some point, multi-folder reading), you're gonna want to get the files inside the menu folder as a list so that's why I say to just get the menus folder as it's own reference (or just get the file without the isolated reference to the parent aka '\menus\').
So your code should really look like this:
public void getMenu() {
final File workingDir = File(System.getProperty("user.dir"));
final File menusDir = File(workingDir, "menus");
final File fastFoodMenu = File(menusDir, "FastFoodMenu.csv");
try {
final FileInputStream fis = new FileInputStream(fastFoodMenu);
final BufferedInputStream bs = new BufferedInputStream(fis);
while((l = bs.readLine()) != null) {
System.out.println(l);
}
} catch(FileNotFoundException e) {
System.out.println(e.getMessage());
e.printStackTrace()
}
}
This is all psuedocode but that should at least get you started. Make sure to use BufferedInputStream for efficiency, and when reading files, always pass them into FileInputStream's. It's much better than using the Scanner class. I should also mention that when creating a File object, you're not actually creating a file. What you're doing is your're creating an object, giving it the data you want it to have (such as whether it's a folder, and if it is, what child files/folders do you want it to have, whether it's protected or not, hidden or not, etc) before actually telling the system to create the file with everything else.
Your csv file is probably at the wrong place. You're just specifying the file name, which is a relative path.
Relative paths are always resolved against the working directory of your application, not against the directory where your source file(s) are.
To solve the issue, you can
move the files to the real working directory.
use an absolute path (not advisable!)
specify the folder of your data files as program argument or in a config file (in your working directory)
put the files somewhere into the classpath of your application and load them from there via classloader. Note that files that are in your classpath are usually packed with your application and hence not easily modifiable by the user, so this solution doesn't work if the file must be changed by the user.

How to only open an existing file in java [duplicate]

This question already has answers here:
How do I check if a file exists in Java?
(19 answers)
Closed 7 years ago.
I would like to know how to open a file in java for writing, but I only want to open it if it exists already. Is that possible? I've searched around and found two suggestions so far which both don't meet my needs. One was to open a file for appending. The problem there is that if the file doesn't exist it will create it. The second was to use file.exists() first. That of course is not a valid solution as the file may exist when I call file.exists() but then not exist when I go to open the file. I want something similar to the Windows API OpenFile() in which I can pass OPEN_EXISTING flag such that the call will fail is the file doesn't exists. Does anything like that exist in java?
Only editing question because it was marked duplicate. Not sure why. I thought I was pretty specific about the other answers and why they were not sufficient.
So I'll restate, I want to open a file for writing. I want the open to fail if the file doesn't already exist.
exists() returns true if the file path is a valid directory even if the file isn't there. You can get around this by using:
File f = new File(filePathString);
if(f.exists() && !f.isDirectory()) {/*Do whatever*/}
or use:
File f = new File(filePathString);
if f.isFile() {/*Do whatever*/}
Just catch the FileNotFoundException that is thrown:
try (FileInputStream fis = new FileInputStream(file))
{
// ...
}
catch (FileNotFoundException exc)
{
// it didn't exist.
}
Solutions involving File.exists() and friends are vulnerable to timing-window problems and therefore cannot be recommended. They also merely duplicate what the OS already has to do, but at the wrong time, so they are a waste of time and space. You have to deal with IOExceptions anyway, so do that.
No there is nothing like that In Java.... Core library
You should be able to wrap your logic in a if statement that names use of the file.exists() method. If you do the check just before opening the file the you would be extremely unlucky if someone has deleted the file in-between. The method that checks if the file exists and the code to open the file and lock it should run in milliseconds..
Eg
If (file.exists() {
//Your Code goes here..
} else {
System.out.Print("missing file");
}

Extract resource folder from running jar in Java 7

My resources folder inside my jar includes a directory with several binary files. I am attempting to use this code to extract them:
try(InputStream is = ExternalHTMLThumbnail.class.getResourceAsStream("/wkhtmltoimage")) {
Files.copy(is, Paths.get("/home/dan/wkhtmltoimage");
}
This is throwing the error
java.nio.file.NoSuchFileException: /home/dan/wkhtmltoimage
Which comes from
if (errno() == UnixConstants.ENOENT)
return new NoSuchFileException(file, other, null);
in UnixException.java. Even though in Files.java the correct options are passed:
ostream = newOutputStream(target, StandardOpenOption.CREATE_NEW,
StandardOpenOption.WRITE);
from Files.copy. Of course there's not! That's why I'm trying to make it. I don't yet understand Path and Files enough to do this right. What's the best way to extract the directory and all its contents?
Confused because the docs for Files.copy claims
By default, the copy fails if the target file already exists or is a symbolic link
(Apparently it fails if the target file doesn't exist as well?)
And lists the possible exceptions, and NoSuchFileException is not one of them.
If you're using Guava:
URL url = Resources.getResource(ExternalHTMLThumbnail.class, "wkhtmltoimage");
byte[] bytes = Resources.toByteArray(url);
Files.write(bytes, new File("/my/path/myFile"));
You could of course just chain that all into one line; I declared the variables to make it more readable.
The file that does not exist may actually be the directory you're trying to create the file in.
/home/dan/wkhtmltoimage
Does /home/dan exist? Probably not if you're on a Mac.

Categories