tags:

views:

101

answers:

1

I've got a ZIP file containing a number of PNG images that I am trying to load into my Java application as ImageIcon resources directly from the archive. Here's my code:

import java.io.*;
import java.util.Enumeration;
import java.util.zip.*;
import javax.swing.ImageIcon;

public class Test {
  public static void main( String[] args )
  {
    if( args.length == 0 )
    {
      System.out.println("usage: java Test.java file.zip");
      return;
    }
    File archive = new File( args[0] );
    if( !archive.exists() || !archive.canRead() )
    {
      System.err.printf("Unable to find/access %s.\n", archive);
      return;
    }

    try {
      ZipFile zip = new ZipFile(archive);
      Enumeration <? extends ZipEntry>e = zip.entries();
      while( e.hasMoreElements() )
      {
        ZipEntry entry = (ZipEntry) e.nextElement();
        int size = (int) entry.getSize();
        int count = (size % 1024 == 0) ? size / 1024 : (size / 1024)+1;
        int offset = 0;
        int nread, toRead;

        byte[] buffer = new byte[size];
        for( int i = 0; i < count; i++ )
        {
          offset = 1024*i;
          toRead = (size-offset > 1024) ? 1024 : size-offset;
          nread = zip.getInputStream(entry).read(buffer, offset, toRead);
        }
        ImageIcon icon = new ImageIcon(buffer); // boom -- why?
      }
      zip.close();      
    } catch( IOException ex ) {
      System.err.println(ex.getMessage());
    }
  }
}

The sizes reported by entry.getSize() match the uncompressed size of the PNG files, and I am able to read the data out of the archive without any exceptions, but the creation of the ImageIcon blows up. The stacktrace:

sun.awt.image.PNGImageDecoder$PNGException: crc corruption
 at sun.awt.image.PNGImageDecoder.getChunk(PNGImageDecoder.java:699)
 at sun.awt.image.PNGImageDecoder.getData(PNGImageDecoder.java:707)
 at sun.awt.image.PNGImageDecoder.produceImage(PNGImageDecoder.java:234)
 at sun.awt.image.InputStreamImageSource.doFetch(InputStreamImageSource.java:246)
 at sun.awt.image.ImageFetcher.fetchloop(ImageFetcher.java:172)
 at sun.awt.image.ImageFetcher.run(ImageFetcher.java:136)
sun.awt.image.PNGImageDecoder$PNGException: crc corruption
 at sun.awt.image.PNGImageDecoder.getChunk(PNGImageDecoder.java:699)
 at sun.awt.image.PNGImageDecoder.getData(PNGImageDecoder.java:707)
 at sun.awt.image.PNGImageDecoder.produceImage(PNGImageDecoder.java:234)
 at sun.awt.image.InputStreamImageSource.doFetch(InputStreamImageSource.java:246)
 at sun.awt.image.ImageFetcher.fetchloop(ImageFetcher.java:172)
 at sun.awt.image.ImageFetcher.run(ImageFetcher.java:136)

Can anyone shed some light on it? Google hasn't turned up any useful information.

+3  A: 

You might must pull getInputStream() out of the inner loop, instead of invoking it repeatedly for each block.

trashgod
That fixed it! (well, not completely, but at least my test case doesn't crash now). I wonder why it worked? I suspect that getInputStream() resets the pointer every time it's called, so I was essentially reading the first 1024 bytes of the PNG over and over again.Thanks!
Nathan Strong
Well I think this is "You must pull" not just "You might". It looks getInputStream() returns a different stream on every call, hence every time read() is called it reads from the beginning of the stream! In the end buffer just contains the first 1KB of the PNG file, duplicated many times!
jfpoilpret
He he I was just 4 seconds too late on that one;-)
jfpoilpret
trashgod