85

I have deployed my app to jar file. When I need to copy data from one file of resource to outside of jar file, I do this code:

URL resourceUrl = getClass().getResource("/resource/data.sav");
File src = new File(resourceUrl.toURI()); //ERROR HERE
File dst = new File(CurrentPath()+"data.sav");  //CurrentPath: path of jar file don't include jar file name
FileInputStream in = new FileInputStream(src);
FileOutputStream out = new FileOutputStream(dst);
 // some excute code here

The error I have met is: URI is not hierarchical. this error I don't meet when run in IDE.

If I change above code as some help on other post on StackOverFlow:

InputStream in = Model.class.getClassLoader().getResourceAsStream("/resource/data.sav");
File dst = new File(CurrentPath() + "data.sav");
FileOutputStream out = new FileOutputStream(dst);
//....
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) { //NULL POINTER EXCEPTION
  //....
}
cheffe
  • 9,345
  • 2
  • 46
  • 57
hqt
  • 29,632
  • 51
  • 171
  • 250

6 Answers6

124

You cannot do this

File src = new File(resourceUrl.toURI()); //ERROR HERE

it is not a file! When you run from the ide you don't have any error, because you don't run a jar file. In the IDE classes and resources are extracted on the file system.

But you can open an InputStream in this way:

InputStream in = Model.class.getClassLoader().getResourceAsStream("/data.sav");

Remove "/resource". Generally the IDEs separates on file system classes and resources. But when the jar is created they are put all together. So the folder level "/resource" is used only for classes and resources separation.

When you get a resource from classloader you have to specify the path that the resource has inside the jar, that is the real package hierarchy.

spottedmahn
  • 14,823
  • 13
  • 108
  • 178
dash1e
  • 7,677
  • 1
  • 30
  • 35
  • I have tried again your way (is my second code above), but when it is go through: `in.read(buf)` ---> I meet null point exception. :( @:resource is a folder in my project. it has same hierarchy with other package. So, I need I must include `resource` – hqt Apr 13 '12 at 17:09
  • 2
    post the hierarchy of your project – dash1e Apr 13 '12 at 17:18
  • 23
    But what if i really NEED an "File" object... ? – TekTimmy Nov 16 '12 at 10:46
  • You cannot have a File object because a resource is not necessary a file. – dash1e Nov 17 '12 at 11:20
  • Could you please provide more details on that? When is a ressource not a file? – Zarathustra Jan 21 '14 at 13:31
  • When the resource is located in a jar, for example, the single `resource` is a Jar Entry object (not a file on file system), and cannot be used to create an instance of `java.io.File` class – dash1e Jan 22 '14 at 05:18
  • @dash1e ^ This is not true - see my answer – Ilya Buziuk Aug 05 '14 at 11:24
  • it works for me without the slash: "data.sav" instead of "/data.sav" – Daniel May 09 '15 at 17:28
  • 2
    @Daniel it depends on where you put your resource. You can use `data.sav` without the starting `/` if you put your resource file in the same package of your class file. But in the question the `data.sav` is in the root package so in that case he needs to prepend `/`. – dash1e May 09 '15 at 19:06
  • 4
    Just like TekTimmy: But what if I REALLY NEED a file ? I'm in a email sending process and we wanna join a logo file picture to email. The pict is in the jar and I have to give it to the datasource as a File, not an InputStream... – JN Gerbaux Jan 20 '17 at 13:25
  • 2
    @JNGerbaux i also wanted a `File` object so i created temp `File` and copied content from `InputStream` to File object. using `FileUtils.copyInputStreamToFile(inputStream, file);` – Vishal Patel Jul 27 '21 at 06:25
  • @VishalPatel that is the right approach if you need the file – dash1e Jul 13 '23 at 10:07
29

If for some reason you really need to create a java.io.File object to point to a resource inside of a Jar file, the answer is here: https://stackoverflow.com/a/27149287/155167

File f = new File(getClass().getResource("/MyResource").toExternalForm());
Community
  • 1
  • 1
Mario
  • 2,397
  • 2
  • 24
  • 41
11

Here is a solution for Eclipse RCP / Plugin developers:

Bundle bundle = Platform.getBundle("resource_from_some_plugin");
URL fileURL = bundle.getEntry("files/test.txt");
File file = null;
try {
   URL resolvedFileURL = FileLocator.toFileURL(fileURL);

   // We need to use the 3-arg constructor of URI in order to properly escape file system chars
   URI resolvedURI = new URI(resolvedFileURL.getProtocol(), resolvedFileURL.getPath(), null);
   File file = new File(resolvedURI);
} catch (URISyntaxException e1) {
    e1.printStackTrace();
} catch (IOException e1) {
    e1.printStackTrace();
}

It's very important to use FileLocator.toFileURL(fileURL) rather than resolve(fileURL) , cause when the plugin is packed into a jar this will cause Eclipse to create an unpacked version in a temporary location so that the object can be accessed using File. For instance, I guess Lars Vogel has an error in his article - http://blog.vogella.com/2010/07/06/reading-resources-from-plugin/

Ilya Buziuk
  • 1,839
  • 5
  • 27
  • 43
  • 1
    What is Platform class? Some non-rt library needed? – Andrey Apr 27 '15 at 15:44
  • 2
    @AndreyP this solution is for eclipse rcp plugin developers. I would recommend to try getResourceAsStream(...) approach – Ilya Buziuk Apr 27 '15 at 15:48
  • 1
    In my maven-built RCP E4 application, jar-bundled binary files need to be extracted and opened. Your solution finally enabled me to do so and open `file.getAbsolutePath()` in the OS default browser. – J. Katzwinkel Mar 30 '16 at 15:02
  • Thanks ! This answer was very helpful, since I got a bug with FileLocator.resolve. I could create the File object directly with : `File f = new File(FileLocator.toFileURL(resolvedFileURL).toURI());` – Rémi Mar 09 '21 at 19:30
1

I got a similiar issues before, and I used the code:

new File(new URI(url.toString().replace(" ","%20")).getSchemeSpecificPart());

instead of the code :

new File(new URI(url.toURI())

to solve the problem

Yukai Tang
  • 11
  • 1
0

While I stumbled upon this problem myself I'd like to add another option (to the otherwise perfect explanation from @dash1e):

Export the plugin as a folder (not a jar) by adding:

Eclipse-BundleShape: dir

to your MANIFEST.MF.

At least when you export your RCP app with the export wizard (based on a *.product) file this gets respected and will produce a folder.

Jens
  • 6,243
  • 1
  • 49
  • 79
0

In addition to the general answers, you can get "URI is not hierarchical" from Unitils library attempting to load a dataset off a .jar file. It may happen when you keep datasets in one maven submodule, but actual tests in another.

There is even a bug UNI-197 filed.

jediz
  • 4,459
  • 5
  • 36
  • 41