简体   繁体   中英

Accessing Java resource inside JAR returns about 75% garbage

I'm trying to load a WAV resource file inside my JAR file. It works perfectly when running via the IDE. But when running via java -jar , it gives me about 75% garbage and 25% of the correct data. I would like to know why. Here are a few methods I tried:

  1. (my preferred method): Read with getResourceAsStream from JAR gives corrupted data that has some parts of the original:

     short[] waveform = AudioFileAPI.readWavFile( Blah.class.getResourceAsStream("/blah.wav")); 
  2. read from regular file: OK

     waveform = AudioFileAPI.readWavFile(new File("C:\\\\blah\\\\blah.wav")); 
  3. extract wav from the created JAR and read it as a regular file: OK

     waveform = AudioFileAPI.readWavFile(new File("C:\\\\blah\\\\fromjar.wav")); 
  4. read with getResource and call play() on AppletAudioClip gives 75% corrupted data as above.

     Object o = Blah.class.getResource("/blah.wav").getContent(); AppletAudioClip appletAudioClip = (AppletAudioClip) o; appletAudioClip.play(); 
  5. read via getResource().openStream(): 75% corrupted data as above.

     InputStream is = Blah.class.getResource("/blah.wav").openStream(); waveform = AudioFileAPI.readWavFile(is); 
  6. unzip and re-zip JAR and re-run the program doesn't help either.

To recap, all above methods work perfectly when run from the IDE, but the indicated ones fail when loading as a resource. The JAR was packaged by IntelliJ IDEA. I use JDK version 1.8.0_131. AudioFileAPI is my own class.

This was actually due to a bug in the code that was reading the resource's InputStream. In cases where the InputStream's read() method did not read all bytes in one go for whatever reason, subsequent read() attempts would incorrectly write into the same buffer always starting at position 0, instead of starting at the position determined by the total number bytes read so far.

So, in the end, the buffer looked like it had fragments of the correct data at the beginning, and garbage at the end.

read() doesn't guarantee to read your file in one go. If you read the documentation for InputReader . it says

Reads some number of bytes from the input stream and stores them into the buffer array b.

Even the read(byte[] b, int off, int len) function signature doesn't guarantee that it will read all len bytes you provide.

Reads up to len bytes of data from the input stream into an array of bytes. An attempt is made to read as many as len bytes, but a smaller number may be read . The number of bytes actually read is returned as an integer.

So you have to loop until available() is zero.

Here's a sample code with also on how to specify a file inside a jar file.

Change jarPath to point to the location of the jarFile. Then change the filePath to point on the file inside the jar. If your file is in

someJar.jar\\img\\test.gif

Set the filePath to "img\\test.gif"

File executable = new File(jarPath);
JarFile jar = new JarFile(executable);
InputStream fileInputStreamReader = jar.getInputStream(jar.getJarEntry(filePath));
byte[] bytes = new byte[fileInputStreamReader.available()];

int sizeOrig = fileInputStreamReader.available();
int size = fileInputStreamReader.available();
int offset = 0;
while (size != 0){
    fileInputStreamReader.read(bytes, offset, size);
    offset = sizeOrig - fileInputStreamReader.available();
    size = fileInputStreamReader.available();
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM