I'm working on a java application which is supposed to load in images from the same directory that the .jar file will be in. The code below is what I currently have, and it works fine in Windows (in the workspace I'm using and in the .jar file's directory, wherever I put it). However, when I try to run the .jar file in OS X, it doesn't work. I get a null pointer exception. Is there something that I'm missing? or some formatting thing I'm not aware of?
String dir = System.getProperty("user.dir");
File folder = new File(dir+"/");
File[] listOfFiles = folder.listFiles();
for (int i = 0; i < listOfFiles.length; i++) {
if (listOfFiles[i].isFile()) {
String name = listOfFiles[i].getName();
String fileType = name.substring(name.length()-3, name.length());
if (fileType.equals("jpg")){
File file = new File(dir+"/"+name);
listMPs.add(new MusicPanel(file));
base.add(listMPs.get(count));
base.add(listMPs.get(count).switchLabel);
if(count==0){
base.add(listMPs.get(0).firstSwitchLabel);
}
assignIndexes();
assignMLs();
count++;
}
}
}
Is there something that I'm missing?
Perhaps you missed that applications should not be storing loose files in the program installation directory. In fact, Sun/Oracle has gone to extreme lengths with applets and JWS launched apps. to ensure that even trusted ones cannot discover that location. Put the files in a more accessible place. A common place is a sub-directory of user.home.
You should be using File.separator instead of manually supplying /s in your paths. Java will format the paths according to the current OS implementation.
Related
I am working on a Java project, which runs fine on Windows 10, but when I tested it in Ubuntu, it shows
"AWT-EventQueue-0" java.lang.NullPointerException: Cannot read the array length because "allFiles" is null.
I read this answer, but could not find a fix.
What I am doing in the project is load an array of images from a certain path. Here is the faulty part of my code:
BufferedImage[] allImages;
public ImageArray(String set, int n) {
File path = new File("res/mnist_png/" + set + "/" + n);
File[] allFiles = path.listFiles();
allImages = new BufferedImage[allFiles.length];
JLabel label[] = new JLabel[allFiles.length];
for (int i = 0; i < allFiles.length; i++) {
try {
allImages[i] = ImageIO.read(allFiles[i]);
label[i] = new JLabel();
ImageIcon icon = new ImageIcon(allImages[i]);
I tried removing the variable allFiles and replacing its use with the actual code it holds but with no success. I saw in the previously answered that the use of the new / this keywords could fix the issue, but I don't seem to be able to find if and where to use them.
I printed the value of the allFiles and path.listFiles() and it is indeed null. Is there a way for the program to work if they remain null? Would changing the null somehow break their intended work?
As I mentioned, the problem occurs only on Linux, but works fine on Windows. Some help would be much appreciated.
First problem: You are using a relative file name. Relative file names have a different meaning depending on the current working directory of the Java process. This is not a Java concept; each process in a system has had its own current directory since long before Java existed.
Second problem: You are trying to list application resources. If you ever choose to package your application as a .jar file, this will not work, because a .jar is a single archive file; the data inside it is all part of one file and they do not constitute actual files.
Relative file names
Any file that does not start with a directory separator (/ on most systems, or optionally \ in Windows) is a relative file name. The actual file location that relative file name refers to depends on the current directory of the Java process. All programs, not just Java programs, work this way.
Some examples:
File name Current directory Actual file location
--------- ----------------- --------------------
res/mnist_png/A/1/image01.png /home/gosho09/project /home/gosho09/project/res/mnist_png/A/1/image01.png
mnist_png /home/gosho09/project /home/gosho09/project/mnist_png
mnist_png / /mnist_png
/home/gosho09/project/res /tmp /home/gosho09/project/res
/home/gosho09/project/res /home/gosho09 /home/gosho09/project/res
/home/gosho09/project/res /usr/local/bin /home/gosho09/project/res
/var/log /tmp /var/log
/var/log /home/gosho09 /var/log
/var/log /usr/local/bin /var/log
As you can see, if the file name does not start with a /, it is relative, and the current directory determines the actual location.
If the file name starts with /, it is considered an absolute file name. Absolute file names are not affected by the current directory.
However… you should not use file names at all.
If you ever want to distribute your application, you will most likely want it to be packaged as a .jar file. A .jar file is a single archive file which contains compiled classes, and application resource files, like your image sets.
Because a .jar file is an archive (it’s actually a specialized zip file), the entries inside it are just parts of the archive, in compressed form. They are not individual files, just sequences of bytes. You cannot read them with the File class.
Fortunately, there is a way to read application resources, which will work both when your application is packaged as a .jar file, and when it exists as regular .class files and data files: the Class.getResource method.
A typical usage might look like this:
String path = "/mnist_png/" + set + "/" + n + "/image" + i + ".png");
URL imageLocation = ImageArray.class.getResource(path);
if (imageLocation == null) {
throw new RuntimeException("Missing resource \"" + path + "\"");
}
allImages[i] = ImageIO.read(imageLocation);
You may be wondering how one is supposed to list files without the File class. The answer is: You can’t and you shouldn’t.
By design, application resources cannot be listed. This is because they are not guaranteed to be listable. Resources are loaded by ClassLoaders, and ClassLoaders may or may not read from directories or from .jar files.
(In fact, the Java SE runtime no longer includes its core classes as a .jar file; as a result, third party tools which used to assume those classes would be available as a .jar file had to be rewritten. Java did not pull the rug out from under those tools’ developers; it was never considered safe to assume classes would come from .jar files, and those developers chose not to heed that warning.)
The alternative to listing the resources is to include a resource which contains a list of the known resource paths. It’s your application; you know what’s in it. So just write a simple plain text listing, include it in your application, and read from that:
String root = "/mnist_png/" + set + "/" + n + "/";
String listingPath = root + "image-list.txt";
try (BufferedReader listing = new BufferedReader(
new InputStreamReader(
Objects.requireNonNull(
ImageArray.class.getResourceAsStream(listingPath),
"Missing resource \"" + listingPath + \""),
StandardCharsets.UTF_8))) {
List<JLabel> labelList = new ArrayList<>();
String path;
while ((path = listing.readLine()) != null) {
URL imageLocation = ImageArray.class.getResource(root + path);
if (imageLocation == null) {
throw new RuntimeException(
"Missing resource \"" + root + path + "\"");
}
labelList.add(new JLabel(new ImageIcon(imageLocation)));
}
labels = labelList.toArray(new JLabel[0]);
}
I'm trying to see how many of these text files exist, but even with them there, the program always says the numFiles = 0. I have the files in a folder called Levels within the src folder. Thanks
int numFiles = 0;
for(int i = 0; i < 24; i++){
File file = new File("/Levels/level" + (i+1) + ".txt");
if(file.exists()){
numFiles++;
}
}
System.out.println(numFiles);
Edited
I overlooked that DirectoryStream doesn't support count()
You could go with an absolute path and make use of Stream API and lambdas. Like so:
String dirString = "..." //absolute Path
Path dir = Paths.get(dirString);
int numFiles = dir.getNameCount();
System.out.println(numFiles);
One advantage is that you can rename the files at will as long as they stay in the same directory. If you only want to work with specific files you can use filter() like so:
Files.newDirectoryStream(dir).filter(Predicate);
or add the filter directly when creating the DirectoryStream like so:
Files.newDirectoryStream(dir, RegEx);
To do something with each File you can use the consumer forEach() or have a look at Stream JavaDoc for other consumers/intermediate operations. Also double check if the DirectoryStream supports the Stream operation you want to use.
Your path is incorrect - if you are referring to an absolute location only then start with a /.
Also if you are using an editor remember your Java files are in src but but you don't run Java File you run class files and the class files may be in your bin/build directory most likely - check if the text file are in the build or bin directory.
Your path is incorrect, if you are referring to a local file(like something in your project folder) use
File file = new File("Levels/level" + (i+1) + ".txt");
the slash you used in front of the name makes it look in the root of the drive, not the local directory.
I am writing a Java program using NetBeans that times the process of developing black and white film. One of the options allows you to set the times manually and the other allows you to select the chosen film and developer to calculate the required times from a CSV file that is loaded into an array.
There are 3 files that are loaded, filmdb.csv, masterdb.csv and usersettings.csv. The first two are loaded purely for reading and the third file is loaded and can be written too to save the users default settings.
All 3 files are stored in the project directory and loaded in a similar way to the following code and called from main:
static String[] filmArray;
static int filmRows = 125;
int selectedDevTime;
int tempDevTime;
int minDevTime;
int secDevTime;
public int count = -1;
static void createFilmArray() {
filmArray = new String[filmRows];
Scanner scanLn = null;
int Rowc = 0;
int Row = 0;
String InputLine = "";
String filmFileName;
filmFileName = "filmdb.csv";
System.out.println("\n****** Setup Film Array ******");
try {
scanLn = new Scanner(new BufferedReader(new FileReader(filmFileName)));
while (scanLn.hasNextLine()) {
InputLine = scanLn.nextLine();
filmArray [Rowc] = InputLine;
Rowc++;
}
} catch (Exception e) {
System.out.println(e);
}
}
When I press the run button in NetBeans all works well, the files are all loaded and stored to the appropriate array but when I build the file as a .jar the files are not loaded, I have tried copying the 3 files to the same directory as the .jar as well as importing the 3 files into the .jar archive but to no joy.
Is there a specific location where the files should be placed, or should they be imported in a different way?
I would rather not load them from an absolute directory as for example a Windows user may have the files stored in C:\users\somebody\devtimer\file.csv and a Linux user may have the files stored in /home/somebody/devtimer/file.csv.
Cheers
Jim
your files got packaged into the *.jar
once a file is in the jar you cannot accessit as a file using a path (since, if you think about it, its no longer a file. its inside another file, the jar file).
you need to do something like this:
InsputStream csvStream = this.getClass().getResourceAsStream("filmdb.csv");
then you can pass this input stream to your csv parser.
you will not be able to make any modifications to the fhile though. any file you want to change you will need to store outside of your jar.
as for file paths, things like new File("somename") are resolved relative to the current working directory. if you want to know where your root is try something like:
System.err.println(new File(".").getCanonicalPath());
this will be very sensitive to what the working directory was when your application was executed, which in turn depends on how exactly it was executed etc.
If you are running the jar from the same directory the csv files are in then it should be loading. There might be some issue though if you just double click on the jar. You could create a new File represented by the filename and check the absolute path to find out where the jar is running from.
If you want to include the csv inside the jar (cleaner for distribution), you need to load the files in a different way. See this question for a nice example.
I use this piece of code to find XML files that another part of my program creates in a given directory:
String fileName;
File folder = new File(mainController.XML_FILES_LOCATION);
File[] listOfFiles = folder.listFiles();
for (int i = 0; i < listOfFiles.length; i++) {
if (listOfFiles[i].isFile()) {
fileName = listOfFiles[i].getName();
if (fileName.endsWith(".xml")) {
Document readFile = readFoundXmlFile(fileName);
if (readFile != null) {
boolean postWasSuccesful = mainController.sendXmlFile(readFile, fileName);
reproduceXmlFile(readFile, fileName, postWasSuccesful);
deleteXmlFile(fileName);
}
}
}
}
What it does is that it reads every XML file that gets placed in the given directory, it sends it to an URL and it copies it to a subdirectory (either 'sent' or 'failed' based on the boolean postWasSuccedful) and deletes the original so it won't be sent again.
In Windows this works as expected, but I've transferred the working code to a Linux machine and all of a sudden it get's in this loop of sending bla.xml and a second later sent\bla.xml and again a second later sent\sent\bla.xml followed by sent\sent\sent\bla.xml, etc.
Why is Linux deciding for itself that listFiles() is recursive?? And, better, how to prevent that? I can add an extra check in the if-statement looking for files ending with .xml that there isn't a directory-char allowed in the fileName, but that's a workaround I don't want as the amount of files in the pick-up directory will never be high whereas the amount of files in the sent subdirectory can get quite high after a while and I wouldn't want this piece of code to become slow
My psychic powers tell me that reproduceXmlFile() builds the target pathname using a hard-coded backslash ("\"), and therefore you're actually creating files with backslashes in their names.
You need to use File.separator rather than that hard-coded "\". Or use something like new File("sent", fileName).toString() to generate your output pathnames.
(Apologies if I'm wrong!)
I'm trying to save a file in a subdirectory in Android 1.5.
I can successfully create a directory using
_context.GetFileStreamPath("foo").mkdir();
(_context is the Activity where I start the execution of saving the file) but then if I try to create a file in foo/ by
_context.GetFileStreamPath("foo/bar.txt");
I get a exception saying I can't have directory separator in a file name ("/").
I'm missing something of working with files in Android... I thought I could use the standard Java classes but they don't seem to work...
I searched the Android documentation but I couldn't fine example and google is not helping me too...
I'm asking the wrong question (to google)...
Can you help me out with this?
Thank you!
I understood what I was missing.
Java File classes works just fine, you just have to pass the absolute path where you can actually write files.
To get this "root" directory I used _context.getFilesDir(). This will give you the root of you application. With this I can create file with new File(root + "myFileName") or as Sean Owen said new File(rootDirectory, "myFileName").
You cannot use path directly, but you must make a file object about every directory.
I do not understand why, but this is the way it works.
NOTE: This code makes directories, yours may not need that...
File file = context.getFilesDir();
file.mkdir();
String[] array = filePath.split("/");
for (int t = 0; t < array.length - 1; t++) {
file = new File(file, array[t]);
file.mkdir();
}
File f = new File(file, array[array.length - 1]);
RandomAccessFileOutputStream rvalue = new RandomAccessFileOutputStream(f, append);
Use getDir() to get a handle on the "foo" directory as a File object, and create something like new File(fooDir, "bar.txt") from it.