Java-Gaming.org Hi !
Featured games (91)
games approved by the League of Dukes
Games in Showcase (803)
Games in Android Showcase (237)
games submitted by our members
Games in WIP (867)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  Editing a text-file stored inside a JAR  (Read 4334 times)
0 Members and 1 Guest are viewing this topic.
Offline Anju_Maaka

Junior Devvie


Medals: 1
Exp: 2 years



« Posted 2014-04-29 14:22:47 »

Ok, I have a .txt file stored inside a folder "res" inside my JAR-file. I can read from that file with the following method.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
   public String[] loadText(String path){
      InputStream mySettingsStream = FileLoader.class.getResourceAsStream("/res/"+path);
      String mySettingsString = "null";
      StringBuilder build = new StringBuilder();
      int i = 0;
      try {
         while((i = mySettingsStream.read()) != -1) {
            build.append((char)i);
         }
         mySettingsString = build.toString();
         return mySettingsString.split("\r\n");
      } catch (IOException e) {
         e.printStackTrace();
         return null;
      }
   }


However, I want to be able to store changes I've made to the data in that file and I can't figure out how to do that. I tried using an OutputStreamWriter with a URL (as you can see below) but it caused a "java.net.UnknownServiceException: protocol doesn't support output" so I clearly need something else.
If possible I want to even be able to create a new .txt file at the location in case one with the name from path doesn't exist.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
   public void write(String path, String[] lines){
      if(path==null || lines==null){return;}
      URL url = FileLoader.class.getResource("/res/" + path);
      try {
         URLConnection connection = url.openConnection();
         connection.setDoOutput(true);
         OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream());
         for(int i = 0; i < lines.length; i++){
            out.write(lines[i]);
         }
         out.close();
      } catch (IOException e) {
         System.err.println("Failed to write to file");
         e.printStackTrace();
      }
   }


Anyone have any ideas?
I know that it might not be the best idea to mess around with changing and creating files inside a JAR file but it'd be great if I could do it safely. At the very least I'd like to have the ability to edit text files in there.
Offline KevinWorkman

« JGO Plugged Duke »


Medals: 288
Projects: 12
Exp: 12 years


HappyCoding.io - Coding Tutorials!


« Reply #1 - Posted 2014-04-29 14:29:24 »

There isn't a great way to do this. Jars are read-only.

You could hack something together that uses a piggybacked jar that you run upon shutdown that repacks a new jar with the file to be saved. Actually I plan on adding that feature to SvgExe, but it's quite a bit of a hack and breaks things like jar signing.

Instead, you might just want to save the file to a temp directory.

HappyCoding.io - Coding Tutorials!
Happy Coding forum - Come say hello!
Offline Anju_Maaka

Junior Devvie


Medals: 1
Exp: 2 years



« Reply #2 - Posted 2014-04-29 15:05:08 »

Maybe that "read only" is the reason why this doesn't work even though I get no errors/exceptions with my code.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
   public void write(String path, String[] lines){
      if(path==null || lines==null){return;}
      URL url = FileLoader.class.getResource("/res/" + path);
      System.out.println("The URL we got is " + url.toString());
      try {
         File file = new File(url.toURI());
         PrintWriter out = new PrintWriter(file);
         for(int i = 0; i < lines.length; i++){
            System.out.println("Appending...");
            out.append(lines[i]);
            out.append("\r\n");  
         }
         out.flush();
         out.close();
      } catch (IOException e) {
         System.err.println("Failed to write to file");
         e.printStackTrace();
      } catch (URISyntaxException e) {
         System.err.println("Failed to write to file");
         e.printStackTrace();
      }
   }


In that case I just have one question. Storing save information outside of the JAR file is ok but I have a wish there. Instead of having a dozen files in the same folder as the JAR, I'd like to create a folder next to the JAR and save them in there. Is there a simple way to check if there is a folder with a certain name next to the JAR and, in case it doesn't exist, creating it?
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline CodeHead

JGO Knight


Medals: 52


From rags to riches...to rags.


« Reply #3 - Posted 2014-04-29 16:26:37 »

...I'd like to create a folder next to the JAR and save them in there. Is there a simple way to check if there is a folder with a certain name next to the JAR and, in case it doesn't exist, creating it?

This should work:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
try {
    // Get the location of the application...
    URL appLocation = this.getClass().getProtectionDomain().getCodeSource().getLocation();
    // Create a URI that puts our new folder location relative to the applications location...
    URI folderLocation = new URL(appLocation, "My/Folder/Location").toURI();
    // We'll let the file object handle the rest of the heavy lifting...
    File f = new File(folderLocation);
    // Does the file/folder already exist...?
    if (!f.exists()) {
        // If not, try to create it...
        if(!f.mkdirs()) {
            // If we find ourselves here, something went wrong when creating the directory
            // structure...act accordingly.
        }
    }
}
catch(MalformedURLException mfu) {
    // Handle MalformedURLException
}
catch(URISyntaxException use) {
    // Handle URISyntaxException
}


IMHO, this isn't the best way to handle it. Consider the scenario where your jar file is being run under Windows. Everything will work as expected as long as the .jar file is in a folder that the user has write access to. If however, the jar file is placed into a system folder such as "Program Files (x86)" or similar, then the application will not be able to create the directory structure, or write to your settings file unless it was launched with administrative privileges. You're probably better off making your settings folder/file(s) in a location somewhere within the users home directory. Just my $0.02. Smiley

Arthur: Are all men from the future loud-mouthed braggarts?
Ash: Nope. Just me baby...Just me.
Offline Anju_Maaka

Junior Devvie


Medals: 1
Exp: 2 years



« Reply #4 - Posted 2014-04-30 11:06:14 »

@CodeHead
URI folderLocation = new URL(appLocation, "My/Folder/Location").toURI();
This worked while in Eclipse, and created new Folders in the "bin". But once exported it didn't work and it instead gave me
"rsrc:My/Folder/Location/test.txt"
Do you have any idea why this happens?
Offline CodeHead

JGO Knight


Medals: 52


From rags to riches...to rags.


« Reply #5 - Posted 2014-04-30 13:37:42 »

Hmm, hard to say. I develop in NetBeans and it worked when ran inside of the IDE as well as when ran as a stand alone application. I can only make a guess that maybe it has something to do with the way Eclipse exports/packages runnable .jar files. Huh

What does the output of the below code look like when run inside of the IDE vs when run from an exported .jar file?
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
try {
    String fs = System.getProperty("file.separator");
    URL appURL = this.getClass().getProtectionDomain().getCodeSource().getLocation();
    String appLocation = URLDecoder.decode(appURL.getFile(), "UTF-8");
    File myFile = new File(new File(appLocation).getParent(), "My" + fs + "Directory" + fs);
    JOptionPane.showMessageDialog(null, myFile.getCanonicalPath());
} catch (UnsupportedEncodingException ex) {
    ex.printStackTrace();
} catch (IOException ex) {
    ex.printStackTrace();
}

Arthur: Are all men from the future loud-mouthed braggarts?
Ash: Nope. Just me baby...Just me.
Offline theagentd
« Reply #6 - Posted 2014-04-30 14:35:42 »

Simply creating a relative File object will point it to the same folder as your JAR:

1  
File file = new File("my text file.txt"); //Ends up right next to the jar.



Example:
1  
FileOutputStream fos = new FileOutputStream("log.txt");


Myomyomyo.
Offline CodeHead

JGO Knight


Medals: 52


From rags to riches...to rags.


« Reply #7 - Posted 2014-04-30 15:21:39 »

Simply creating a relative File object will point it to the same folder as your JAR:

Good catch. Cool I seem to remember that not always being the case (which would explain the plethora of answers to similar questions on the Internet). One caveat that I noticed which is a non issue for the OP's use case is that when run in the IDE (at least in NetBeans), the file is not created in the expected directory. In my case, the file was created in the top level of the project folder whereas the verbose method created the file in the same directory as the .class file. Either way, your method is definitely a lot cleaner. LOL

Arthur: Are all men from the future loud-mouthed braggarts?
Ash: Nope. Just me baby...Just me.
Offline Riven
Administrator

« JGO Overlord »


Medals: 1371
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #8 - Posted 2014-04-30 15:23:04 »

Simply creating a relative File object will point it to the same folder as your JAR:

1  
File file = new File("my text file.txt"); //Ends up right next to the jar.

Not on Mac OS X. The workdir will be ~ (user home)

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings!
Offline theagentd
« Reply #9 - Posted 2014-04-30 18:43:31 »

Not on Mac OS X. The workdir will be ~ (user home)
Are you sure? I've had my engine tested on OS X and I haven't had any problem with people saying they can't find the log file when I ask for it...

Myomyomyo.
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Danny02
« Reply #10 - Posted 2014-04-30 21:39:10 »

With Java 7, which added Filesystem implementations in nio2, there is a something called ZipFileSystem.

You can open a zip file as a Filesystem and then do normal File operations as you do all the time, which operate directly on the Jar.
An URL to your running Jar can be retrieved by the getURLs method of the classloader.
Pages: [1]
  ignore  |  Print  
 
 

 
Riven (397 views)
2019-09-04 15:33:17

hadezbladez (5280 views)
2018-11-16 13:46:03

hadezbladez (2204 views)
2018-11-16 13:41:33

hadezbladez (5544 views)
2018-11-16 13:35:35

hadezbladez (1150 views)
2018-11-16 13:32:03

EgonOlsen (4584 views)
2018-06-10 19:43:48

EgonOlsen (5462 views)
2018-06-10 19:43:44

EgonOlsen (3119 views)
2018-06-10 19:43:20

DesertCoockie (4015 views)
2018-05-13 18:23:11

nelsongames (4708 views)
2018-04-24 18:15:36
A NON-ideal modular configuration for Eclipse with JavaFX
by philfrei
2019-12-19 19:35:12

Java Gaming Resources
by philfrei
2019-05-14 16:15:13

Deployment and Packaging
by philfrei
2019-05-08 15:15:36

Deployment and Packaging
by philfrei
2019-05-08 15:13:34

Deployment and Packaging
by philfrei
2019-02-17 20:25:53

Deployment and Packaging
by mudlee
2018-08-22 18:09:50

Java Gaming Resources
by gouessej
2018-08-22 08:19:41

Deployment and Packaging
by gouessej
2018-08-22 08:04:08
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!