6

I'm trying to access a resource from the class path/JAR file as a File object. I'm aware that it's the preferred method to use an InputStream object instead, but I'm using an external library (WorldEdit) which needs a File object.

Here's my code:

InputStream templStream = "".getClass().getResourceAsStream("/res/template.prom");
System.out.println("templateStream: " + templStream.toString());
File templFile = new File("".getClass().getResource("/res/template.prom").toURI());
System.out.println("templateFile: " + templFile.canRead());

Now while I'm still inside eclipse, both ways of accessing the resource work flawlessly and produce this output:

templStream: java.io.BufferedInputStream@746d1683
templFile: true

But after exporting the code into a JAR archive, the code fails:

templStream: sun.net.www.protocol.jar.JarURLConnection$JarURLInputStream@47aa261b
Exception in thread "main" java.lang.IllegalArgumentException: URI is not hierarchical
    at java.io.File.<init>(File.java:392)
    at SMCToPROM.main(SMCToPROM.java:101)

So I've been, without success, searching for a way for either accessing the resource as a File directly, or going the way of using an InputStream and converting that InputStream to a file.

The worst case fallback solution would be to copy the InputStream to a file on the filesystem, and then open that file, but I hope that won't be neccesary.

mic_e
  • 5,594
  • 4
  • 34
  • 49
  • possible duplicate of [Java Jar file: use resource errors: URI is not hierarchical](http://stackoverflow.com/questions/10144210/java-jar-file-use-resource-errors-uri-is-not-hierarchical) – assylias May 15 '12 at 16:42
  • @assylias: There are lots of questions related to this issue on this site, but all answers I've found basically say 'just use a InputStream instead'. For me, that's not an option, which is why my question is if there's any way, except the wort-case approach I've stated, of accessing it as a File. – mic_e May 15 '12 at 16:47

3 Answers3

5

The short answer is that you can't, because the resource isn't a File.

Either the third-party library is badly written if all it wants to do is read data from a source (and thus it should take an InputStream), or the library actually wants to do File-specific manipulations.

Assuming this isn't an oversight in the library that can be fixed, you'll need to actually create a file yourself. Try calling File.createTempFile(), populate this file with the contents of the resource yourself, and then pass this File object to the library. (Delete it once you're done, of course).

Andrzej Doyle
  • 102,507
  • 33
  • 189
  • 228
3

You cannot access the jarred resources as Files directly. Instead you need to copy the content of the resource to an actual file and pass that to the 3rd party library (you can use the InputStream you get from getResourceAsInputStream() for this)

Attila
  • 28,265
  • 3
  • 46
  • 55
1

You could write a File wrapper object that is backed by an InputStream, Byte Array or ByteBuffer and then pass that into the poorly written library code.

This won't be simple and would require you writing a correct implementation of every method if you have no idea what methods that library is calling on the File object.

I did this once in a similar situation, but I knew the sub-set of calls and didn't have to implement every method correctly. You might get lucky and the only thing the library does is call .getInputStream() and be done with it easily.

  • The only thing the 3rd party library is doing is invoke the FileInputStream constructor with the File as an argument. Could you please post (the important parts of) your code on pastebin? – mic_e May 15 '12 at 17:32
  • That code is long gone at a previous employer, but just because all you can see is the `FileInputStream` constructor, doesn't mean there aren't other calls on `File` that aren't needed. The source code for `FileInputStream` shows it is more complicated than first glance because `FileDescriptor` is used as well. The library being hard coded for a specific implementation of `InputStream` is even worse than than being tied to `File` directly. Whomever wrote this library needs to review their OOD skills. –  May 15 '12 at 17:40
  • The FileInputStream is then immediately passed to a GZIPInputStream constructor. – mic_e May 15 '12 at 17:59
  • They should have just used `InputStream` instead of `File` and you would be having this issue. The source of `GZIPInputStream` only shows to constructors and both are `InputStream` so you might can use my solution to expose only the `.getInputStream()` and have it work. –  May 15 '12 at 18:11