Obtaining a usable Url.getFile() from a Resource?

I have successfully obtained a Url from a resource. But url.getFile() returns different things in different contexts. For example, in my Eclipse IDE:

/C:/Documents%20and%20Settings/Phil/workspace/Fivefold/bin/com/adonax/fivefold/audio/elders.ogg

If I’m running the same code from a Jar:

file:/C:/Documents%20and%20Settings/Phil/Desktop/Java/fivefold/ffogg.jar!/com/adonax/fivefold/audio/elders.ogg

Note the presence of the “!” in this one.

So, it is possible to do the following which works in both contexts (though it really feels like a kluge):

String s = s.replace("%20", " "); 
s = s.replace("!", "");
s = s.replace("file:", "");
s = s.substring(1);

But I sure would like to know either or both:

  1. what the heck is going on with getFile(), why is it adding these things and what will happen when I try to do this in another OS or as an Applet?
  2. is there a better way to get a filename for a working “resource” that is in a Jar?

Reason for question: there is a function in JCraft’s JOrbis that allows one to obtain a frame count but I can only make it work for VorbisFile objects created with a filename string. When I make a VorbisFile object with an InputStream, it decides the file is not “seekable”. (I would have to dig deeper into the JOrbis code to find out why this is.) If I could get that framecount another way, that would make the above question moot, but still kind of interesting to me. The whole Jar/File/Resource complex has always been a toughie.

Many thanks!

Hello,
I’m not sure if I understand your question correct…
But the main difference is, that in your first code-line you loaded the File from outside a jar-file and from the second code-line from inside a jar-file. That is because you use the class.getRessource() method, that uses the path from your ressource, which can be inside a jar-file, which means the ! in your second code-line.

To the Question what happens on another OS, I would guess That the link is a little changed, because it has another filesystem, Linux par example has “” as it’s root folder, while windows has no root-folder, the nearest thing to the root-folder on windows is the C:/ volume, so your first link would look like:
1

file:/\usr\Phil…\workspace\Fivefold\bin\com\adonax\fivefold\audio\elders.ogg

the / after file:/ stands for the root-folder, please notice that linux or unix all in all doesn’t use / for the folder-file seperator, but \ for them!!!
usr is the Folder that inhabits the user-homefolders. (The name can change from Linux OS to Linux OS)

To the second the Question:
I’m not sure why you need a better way? Do you get an error when you use this command to open your ogg File? Because your file-link should be changed acording to your OS under your JRE, so that the command should work everywhere.

Hope my knowledge helps.

P.S. I use Windows as my main OS, so I hope for a comment from someone who uses Linux more often!

biro

You can’t obtain a file from within a jar-archive (the ! is the separator between the location of the containing jar file and the path inside the archive of the contained file).

So if you really need the file, you have to read it from the InputStream and write it to some temp file you can use afterwards.

??? Linux uses / as a path separator. Windows uses , although in some contexts (e.g. url address bars) / will be translated. /usr usually contains data (system resources). The user directories are in /home on most (all?) Linux distros; on OS X they’re in /Users; not sure about other Unices.

Hello,

[quote]Linux uses / as a path separator. Windows uses , although in some contexts (e.g. url address bars) / will be translated. /usr usually contains data (system resources). The user directories are in /home on most (all?) Linux distros; on OS X they’re in /Users; not sure about other Unices.
[/quote]
sr, that I got that one wrong, but I only said how I remembered it to be, and it looks like it was wrong -> That’s the reason why I asked for an answer from someone who uses Linux more often :smiley:

[quote]You can’t obtain a file from within a jar-archive (the ! is the separator between the location of the containing jar file and the path inside the archive of the contained file).

So if you really need the file, you have to read it from the InputStream and write it to some temp file you can use afterwards.
[/quote]
Sorry to correct you… but you can. I often get my pictures out of my jar Files, and other things too. I think you meant, that you can’t ADD things to your jar-Archive while you’re using it.

biro

I guess a BufferedInputStream should be “seekable”? Maybe try this:


InputStream is = Game.class.getClassLoader().getResourceAsStream("sounds/" + sound);
BufferedInputStream bis = new BufferedInputStream(is);

Are there actually any cases where ‘/’ won’t work in windows? I just use / everywhere and have never run into a problem. Using File.pathSeparator everywhere is just plain ugly.

It’s not about getting the file data out of a jar file, but about getting a valid file location of a resource inside a jar to be able to feed it to APIs working with filenames instead of InputStreams.

Since the resource is inside another file (the jar) you cannot obtain a valid file location. It simply does not exist on the computers filesystem - only the containing jar does.

Forward slashes work fine on Windows. The only cases where I use the system-dependent path separator are when I need to communicate the path to the user.

To sum up:
-You can’t obtain a java.io.File that points to a resource inside of another file, like a zip file or a Jar file.
-Unix uses “/” and Windows uses “” as a default but also allows “/”, however beware that “/” is also an escape character on Windows, while “” is an escape character on Unix.

Ok? Beautiful :smiley:

Erm… No :wink: “” is an escape character…

“/” is an escape character on Windows, “” is an escape character on Unix.

Thanks everyone!

I also asked the question over at JavaRanch http://www.coderanch.com/t/540718/Streams/java/curious-stuff-returned-Url-getFileand got basically the same answer: a file packed in a Jar isn’t a file. Alternative courses of action: 1) rewrite the constructor of VorbisFile to accept a URL or InputStream, 2) make a temp copy of the resource and access that as a File.

@m-waddams – I tried your suggestion, converting to a BufferedInputStream. Simple substitution did not work, unfortunately. Digging a bit further, what is going on in JOrbis is to create a RandomAccessFile from the file location string. The “seekable” boolean is only set if a RandomAccessFile has been created. And I don’t see any way to create a RandomAccessFile from a URL, only File constructors.

@biro – To clarify, I can open and play an Ogg just fine. My goal is to query the file’s header to find out, in advance, how many frames are contained in the file. JCraft’s JOrbis implementation exposes this info via an object called VorbisFile. This object is not involved in playback! It’s constructor relies on a File name string.

Continuing on a second post…to follow shortly…

continuing from previous post…

So, I’m considering a few different options.

  1. Parse the URL.getFile() into a “useable” string. Seems like a kluge and asking for trouble, but it also seems to work. The question is how well will it work in the future, and will it work for various OSes. I should post a trial file and ask people with Mac or Linux to try it out. The various replies (thank you, again!) about different ways of displaying files has me thinking it might work but it might be a little dicey. [EDIT: suddenly this approach stopped working. So, was I hallucinating when I thought it WAS working? ???]

  2. Go ahead and do a temp file write. This might not be too costly, as in the Audio subsystem I am creating, this would only occur with the creation of the higher level objects (I’m implementing an “improved” Loop player and a Chaining sort of player to allow “branching” background music or sound events). There would be no cost at the point in time where the OggVorbisCueWrapper is called to play. Also, the temp file remains compressed. We aren’t decoding and writing the full-sized file.

  3. Investigate some code I found in Horstmann using ZipInputStream. He has some example code that displays the contents of a Zip archive and allows the selection of a file within that archive. (Vol. II, Eighth Ed., pg. 34.) I’m guessing that if this can be successfully implemented, it would be the fastest and safest option. I will report back if I am successful. [EDIT: fuzzy thinking. Yes, can inspect inside a zip/jar and identify relative file names. But the file is still inside a jar and not available as a RandomAccessFile.]

  4. Continue to dig inside of JOrbis for ways to find where this data I want is stored and if there is another way to get at it.

The discussion below really highlights, to my mind, the ambiguity and confusion around resources packed in Jars. When is a File not a File? I can understand the statement that the “file name” of the packed resource is not a “valid file location”. Yet I find that I can create a string that allows Java to open a File that is in a Jar, albeit with a bunch of kluges like converting “%20” to " " and eliminating “!” & “file:” and a starting “/”. Does that fact contradict the first statement? This seems paradoxical to me. I also find it hard to understand how a file thus opened is even readable, since presumably its contents are in a Zipped format rather than in its original form.

Have you tried looking at the source?

http://www.docjar.org/html/api/java/net/URL.java.html

Nice. Bookmarked the site.

If JOrbis is open source and you don’t mind forking it (whether or not you offer the patch back to them), one approach would be to add another constructor which takes a byte[]. This would wind up being roughly equivalent to the read-the-stream-and-write-it-to-disk approach, except without the write-it-to-disk. Of course, it’s a matter of trading off memory usage against disk access.

The problem with trying to work directly with the zip is that random access into a zipped file will require you to scan the whole thing and build up an index, at which point you may as well have simply decompressed it. The only way this could reasonably work is if the compression method used for the file is NONE, which is a bit brittle.

You (and the JOrbis guys) have got things backwards IMHO. Files are just for physical, local files. What you really want to be doing is using Urls (or URIs) everywhere. Urls are a superset of Files - they can be paths to things on the internet, on a local file system, or in a virtual file system (ie. a file within a jar).

The best solution would be to change JOrbis to accept an Url rather than a File. But if you don’t want to do that then extracting it to a temp dir is probably your best solution.

If he changes JOrbis then he’ll just be doing the same thing of extracting to a temp location inside the library rather than outside it. If you look back up, the reason it wants a File is to create a RandomAccessFile. There’s no interface

interface IRandomAccessByteSource
{
    void seek(long pos);
     int read(byte[] b, int off, int len);
}

which can be used instead, and even if you add an interface there’s no general mechanism for implementing it around a URL other than reading the entire contents of the URL to a temporary byte[] or file.