2

This is the code

public static void readCharacters() {

    try (FileInputStream fi = new FileInputStream("main/characters.dat"); ObjectInputStream os = new ObjectInputStream(fi)) {

        characterList = (LinkedList<Character>) os.readObject();

    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
}

This is the structure: enter image description here

And this is the Error

java.io.FileNotFoundException: main\characters.dat (The system cannot find the path specified)

What I want is to include the characters.dat file in my jar, and be able to read and write it while the program runs. Is there a different way to write the path? or to put the .dat file in a different position.

Also the writing method:

public static void writeCharacters() {
    try (FileOutputStream fs = new FileOutputStream("main/characters.dat"); ObjectOutputStream os = new ObjectOutputStream(fs)) {
        System.out.println("Writing Characters...");
        os.writeObject(characterList);

    } catch (FileNotFoundException e) {

        e.printStackTrace();
    } catch (IOException e) {

        e.printStackTrace();
    }

}
Syarx
  • 69
  • 1
  • 8

3 Answers3

3

You can't. You can do one or the other. JAR files are not file systems, and their entries are not files. You can read it with an input stream:

InputStream in = this.getClass().getResourceAsStream("/main/characters.dat");

Check it for null before proceeding.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • what do you mean i can do one or the other? – Syarx Apr 03 '17 at 22:29
  • You can either use a `FileInputStream` *or* place the resource in a JAR file. – user207421 Apr 03 '17 at 22:31
  • any alternative to the FileInputStream? I posted the writing code too if it changes anything – Syarx Apr 03 '17 at 22:34
  • 1
    Eh? *I have given you an alternative.* Your 'writing code' doesn't actually work. You can't write *into* a JAR file, for the same reason as above. – user207421 Apr 03 '17 at 22:35
  • Yes your reading method works, but if I understand correctly, I won't be able to re-write the changes in the file while it is in a jar. How can I bypass that, how can i have the file outside the jar but still be able to use relative paths? – Syarx Apr 03 '17 at 22:49
  • Different question. See [here](http://stackoverflow.com/questions/4032957/how-to-get-the-real-path-of-java-application-at-runtime/4033090#4033090). – user207421 Apr 03 '17 at 22:53
1

The jar is for read-only resources. You can use it for the initial file, as a kind of template.

Path path = Paths.get(System.getProperty("user.home") + "/myapp/chars.dat");
Files.mkdirs(path.getParentPath());
if (!Files.exists()) {
    try (InputStream in =
            Controller.class.getResourceAsStream("/main/characters.dat")) {
         Files.copy(in, path);
    }
}

The above copies the initial.dat resource from the jar to the user's home "myapp" directory, which is a common solution.

System.getProperty("user.dir") would the running directory. One can also take the jar's path:

URL url = Controller.class.getResource("/main/characters.dat");
String s = url.toExternalForm(); // "jar:file:/.... /xxx.jar!/main/characters.dat"

From that you can also construct the jar's directory. Mind to check Windows, Linux, spaces and such.

URL url = Controller.class.getProtectionDomain().getCodeSource().getLocation();

The solution above risks a NullPointerException, and works a bit differenly running inside the IDE or stand-alone.

Joop Eggen
  • 107,315
  • 7
  • 83
  • 138
  • Is there a way to be able to have the .dat file outside the jar and the jar along with it on different computers, and for it to be able to run without extra configuration for each move? – Syarx Apr 03 '17 at 22:31
  • For a movable application with its data I have added some manners still needing some caution. – Joop Eggen Apr 04 '17 at 21:35
1

Important note:

When using getResourceAsStream, you must start your path by slash /, this specifies the root of your jar, .getResourceAsStream("/file.txt");

In my case my file was a function argument, String filename, I had to do it like this:

InputStream in = this.getClass().getResourceAsStream("/" + filename);
António Almeida
  • 9,620
  • 8
  • 59
  • 66