4 Replies Latest reply on Oct 14, 2012 7:12 PM by 968215

    Faulty decompression with java.util.zip

      I want to use java.util.zip classes to compress the content of files (BMP pictures) and later decompress them for displaying. Strange is that usihg the same method some files decompress correctly, another not.
      I have written the test code for the compression/decompression of the content of a file (input parameters are "c file", compressed file is given the ".comp" extension that is removed on decompression). Here are two test images. Following (compressing the file & decompressing the result):
      TestCompDecomp c A.bmp
      TestCompDecomp d A.bmp.comp ... is OK
      but the same process with B.bmp leads to corrupted image.
      package testcompdecomp;
      import java.io.File;
      import java.io.FileInputStream;
      import java.io.FileOutputStream;
      import java.io.IOException;
      import java.io.InputStream;
      import java.util.zip.DataFormatException;
      import java.util.zip.Deflater;
      import java.util.zip.Inflater;
      public class TestCompDecomp {
           static class Compressor {
                // compresses byte array, returns compressed bytes in array
                public byte[] compress(byte[] bytesToCompress){          
                     Deflater compressor = new Deflater(java.util.zip.Deflater.BEST_COMPRESSION);
                     byte[] bytesCompressed = new byte[2000000];
                     int numberOfBytesAfterCompression = compressor.deflate(bytesCompressed);
                     byte[] returnValue = new byte[numberOfBytesAfterCompression];
                     System.arraycopy(     bytesCompressed, 0,     returnValue,     0, numberOfBytesAfterCompression);
                     return returnValue;
                // decompresses byte array, returns decompressed bytes in array
                public byte[] decompress(byte[] bytesToDecompress) throws DataFormatException {
                     Inflater decompressor = new Inflater();
                     int numberOfBytesToDecompress = bytesToDecompress.length;
                     decompressor.setInput( bytesToDecompress, 0, numberOfBytesToDecompress);
                     int compressionFactorMaxLikely = 3;
                     int bufferSizeInBytes = numberOfBytesToDecompress * compressionFactorMaxLikely;
                     byte[] bytesDecompressed = new byte[bufferSizeInBytes];
                     int numberOfBytesAfterDecompression = decompressor.inflate(bytesDecompressed);          
                     byte[] returnValue = new byte[numberOfBytesAfterDecompression];
                     System.arraycopy( bytesDecompressed, 0, returnValue, 0, numberOfBytesAfterDecompression);     
                     return returnValue;
           public static void main(String[] args) throws IOException, DataFormatException {
                if (args.length < 2) {
                     System.out.format("usage: TestCompDecomp <command> <file>%n%n");
                     System.out.format("<commands>%n  c   compress file%n  d   decompress file%n");
                byte[] input = null;
                byte[] output = null;
                //     read file content into array
                File inputFile = new File(args[1]);
                try (InputStream stream = new FileInputStream(inputFile)) {
                     long length = inputFile.length();
                     input = new byte[(int)length];
                     int offset = 0, numRead = 0;
                     while (offset < input.length && numRead >= 0) {
                          numRead = stream.read(input, offset, input.length-offset);
                          offset += numRead;
                } catch (IOException e) {
                     System.err.println(args[1]+": read error");
                // process the file content
                Compressor compressor = new Compressor();
                switch (args[0]) {
                     case "c":     // compression
                          output = compressor.compress(input);
                     case "d":     // decompression
                          output = compressor.decompress(input);
                     default: ;
                // write the processed bytes to tne output file
                FileOutputStream outFile = null;
                String outFileName = args[1];
                switch (args[0]) {
                     case "c":
                          outFileName += ".comp";
                     case "d":
                          outFileName = outFileName.replace(".comp", "");
                try {
                     outFile = new FileOutputStream(outFileName);
                } finally {
                     if (outFile != null)
      Any idea please?

        • 1. Re: Faulty decompression with java.util.zip
          int compressionFactorMaxLikely = 3;
          Decompression will be truncated if the file was compressed better than this.
          1 person found this helpful
          • 2. Re: Faulty decompression with java.util.zip
            Welcome to the forum!
            I want to use java.util.zip classes to compress the content of files (BMP pictures) and later decompress them for displaying. Strange is that usihg the same method some files decompress correctly, another not.
            That's pretty much what you should expect when you write code that has absolutely NO error checking or parameter checking at all.

            GIGO - Garbage In, Garbage Out.

            Your compress method uses a byte array for a parameter. That byte array could be of any legal size.

            Then you HARD-CODE the size of the target/compressed byte array to 2 million bytes. You are just keeping your fingers crossed that the compressed size will fit into this buffer. And your method doesn't check for any errors at all.

            What makes you think that an input byte array that is 1 GB in size will compress into 2 million bytes?

            Then you make similar assumptions for the decompress method; you ASSUME that the decompressed size of the data will not be more than three times the compressed size.
            int compressionFactorMaxLikely = 3;
            int bufferSizeInBytes = numberOfBytesToDecompress * compressionFactorMaxLikely;
            byte[] bytesDecompressed = new byte[bufferSizeInBytes];
            And you already know what is going to happen if your assumtions are wrong.

            If you want to learn to write good code you can't make assumptions like you made. If you do make assumptions you have to add code to validate that your assumptions are correct, to check the length of the arrays being passed and add error handling so that you know when things go wrong.

            The above would be bad enough but the biggest mistake you are making is reinventing the wheel. You are writing custom code to implement functionality that Java already provides for you.

            The best solution to your problem is to just create a standard zip file from your input. ALWAYS, ALWAYS, ALWAYS start with the simplest solution that will meet your requirements.

            A zip file solution:

            1. Uses out of the box Java functionality - you only need to write code that interfaces with the API classes
            2. Allows you to read/write files of any arbitrary size without having to allocate buffers of unknown size.
            3. Has a very minimal overhead in the zip file for the central directory structure and zip entry data that is needed for one file.
            4. Makes it easy to inspect the zip file contents since the file can be opened with ANY zip utililty.
            5. Makes it easy to test the read and write functionality separately. Just use Winzip or some other utility to create a zip of a sample BMP file and then test your code to see if you can unzip it.
            6. Lets you work with input/output streams instead of byte arrays.

            I suggest you abandon your current approach and implement a zip read/write utility that provides the interface to your BMP files that you need. Only if that doesn't ultimately meet your needs should you use a custom approach.

            There are plenty of simple examples on the web that show how to create, read and write zip files using Java. Here are two:
            • 3. Re: Faulty decompression with java.util.zip
              Thanks, you are right... the output buffer was too small for the better compressed image. I could have realize that :(
              • 4. Re: Faulty decompression with java.util.zip
                Many thanks for your comprehensive reply. The supplied testing piece of code was got rid of all error checking for the length purposes. The crucial bug was the assuntion underrating decompress output buffer
                int compressionFactorMaxLikely = 3;
                The most precious is your proposal to rewrite this legacy code using standard Java functionality :)
                Thank you once more.