Zlib compression Using Deflate and Inflate classes in Java
Asked Answered
C

2

13

I want trying to use the Deflate and Inflate classes in java.util.zip for zlib compression.

I am able to compress the code using Deflate, but while decompressing, I am having this error -

Exception in thread "main" java.util.zip.DataFormatException: unknown compression method
    at java.util.zip.Inflater.inflateBytes(Native Method)
    at java.util.zip.Inflater.inflate(Inflater.java:238)
    at java.util.zip.Inflater.inflate(Inflater.java:256)
    at zlibCompression.main(zlibCompression.java:53)

Here is my code so far -

import java.util.zip.*;
import java.io.*;

public class zlibCompression {

    /**
     * @param args
     */
    public static void main(String[] args) throws IOException, DataFormatException {
        // TODO Auto-generated method stub

        String fname = "book1";
        FileReader infile = new FileReader(fname);
        BufferedReader in = new BufferedReader(infile);

        FileOutputStream out = new FileOutputStream("book1out.dfl");
        //BufferedInputStream in = new BufferedInputStream(new FileInputStream(filename));

        Deflater compress = new Deflater();
        Inflater decompress = new Inflater();

        String readFile = in.readLine();
        byte[] bx = readFile.getBytes();

        while(readFile!=null){
            byte[] input = readFile.getBytes();
            byte[] compressedData = new byte[1024];
            compress.setInput(input);
            compress.finish();
            int compressLength = compress.deflate(compressedData, 0, compressedData.length);
            //System.out.println(compressedData);
            out.write(compressedData, 0, compressLength);
            readFile = in.readLine();
        }

        File abc = new File("book1out.dfl");
        InputStream is = new FileInputStream("book1out.dfl");

        InflaterInputStream infl = new InflaterInputStream(new FileInputStream("book1out.dfl"), new Inflater());
        FileOutputStream outFile = new FileOutputStream("decompressed.txt");

        byte[] b = new byte[1024];
        while(true){

            int a = infl.read(b,0,1024);
            if(a==0)
                break;

            decompress.setInput(b);
            byte[] fresult = new byte[1024];
            //decompress.in
            int resLength = decompress.inflate(fresult);
            //outFile.write(b,0,1);
            //String outt = new String(fresult, 0, resLength);
            //System.out.println(outt);
        }

        System.out.println("complete");

    }
}
Cladophyll answered 30/5, 2011 at 8:25 Comment(1)
is that a homework? one mistake is calling finish too early, another other is using setInput w/o length and one more is not checking if the deflate process has returned more data than the 1024.Mylohyoid
F
31

What are you trying to do here? You use an InflaterInputStream, which decompresses your data, and then you try to pass this decompressed data again to an Inflater? Use either one of them, but not both.

This is what is causing your exception here.

In addition to this, there are quite some minor errors, like these mentioned by bestsss:

  • You finish the compression in the loop - after finishing, no more data can be added.
  • You don't check how much output the deflate process produces. If you have long lines, it could be more than 1024 bytes.
  • You set input to the Inflater without setting the length a, too.

Some more which I found:

  • You don't close your FileOutputStream after writing (and before reading from the same file).
  • You use readLine() to read a line of text, but then you don't add the line break again, which means in your decompressed file won't be any line breaks.
  • You convert from bytes to string and to bytes again without any need.
  • You create variables which you don't use later on.

I won't try to correct your program. Here is a simple one which does what I think you want, using DeflaterOutputStream and InflaterInputStream. (You could also use JZlib's ZInputStream and ZOutputStream instead.)

import java.util.zip.*;
import java.io.*;

/**
 * Example program to demonstrate how to use zlib compression with
 * Java.
 * Inspired by https://mcmap.net/q/847972/-zlib-compression-using-deflate-and-inflate-classes-in-java/600500.
 */
public class ZlibCompression {

    /**
     * Compresses a file with zlib compression.
     */
    public static void compressFile(File raw, File compressed)
        throws IOException
    {
        InputStream in = new FileInputStream(raw);
        OutputStream out =
            new DeflaterOutputStream(new FileOutputStream(compressed));
        shovelInToOut(in, out);
        in.close();
        out.close();
    }

    /**
     * Decompresses a zlib compressed file.
     */
    public static void decompressFile(File compressed, File raw)
        throws IOException
    {
        InputStream in =
            new InflaterInputStream(new FileInputStream(compressed));
        OutputStream out = new FileOutputStream(raw);
        shovelInToOut(in, out);
        in.close();
        out.close();
    }

    /**
     * Shovels all data from an input stream to an output stream.
     */
    private static void shovelInToOut(InputStream in, OutputStream out)
        throws IOException
    {
        byte[] buffer = new byte[1000];
        int len;
        while((len = in.read(buffer)) > 0) {
            out.write(buffer, 0, len);
        }
    }


    /**
     * Main method to test it all.
     */
    public static void main(String[] args) throws IOException, DataFormatException {
        File compressed = new File("book1out.dfl");
        compressFile(new File("book1"), compressed);
        decompressFile(compressed, new File("decompressed.txt"));
    }
}

For more efficiency, it might be useful to wrap the file streams with buffered streams. If this is performance critical, measure it.

Fricke answered 15/6, 2011 at 23:46 Comment(2)
are you sure this is for zlib. I try to decompress a zlib file but i get a unknown compression method error. If i look at docs.oracle.com/javase/1.4.2/docs/api/java/util/zip/… then i see Inflater mentioning zlib but you use InflaterInputStream which mentions deflate compression method. This is the file: dl.dropboxusercontent.com/u/17630770/temp/…Ahron
InflaterInputStream uses (if using the constructor without Inflater parameter) a new Inflater(). And this constructor uses nowrap = false, which means the zlib-compression. (I didn't check your file.)Ruggiero
C
4

Paŭlo Ebermann's code can be further improved by using try-with-resources:

import java.util.Scanner;
import java.util.zip.*;
import java.io.*;

public class ZLibCompression
{
    public static void compress(File raw, File compressed) throws IOException
    {
        try (InputStream inputStream = new FileInputStream(raw);
             OutputStream outputStream = new DeflaterOutputStream(new FileOutputStream(compressed)))
        {
            copy(inputStream, outputStream);
        }
    }

    public static void decompress(File compressed, File raw)
            throws IOException
    {
        try (InputStream inputStream = new InflaterInputStream(new FileInputStream(compressed));
             OutputStream outputStream = new FileOutputStream(raw))
        {
            copy(inputStream, outputStream);
        }
    }

    public static String decompress(File compressed) throws IOException
    {
        try (InputStream inputStream = new InflaterInputStream(new FileInputStream(compressed)))
        {
            return toString(inputStream);
        }
    }

    private static String toString(InputStream inputStream)
    {
        try (Scanner scanner = new Scanner(inputStream).useDelimiter("\\A"))
        {
            return scanner.hasNext() ? scanner.next() : "";
        }
    }

    private static void copy(InputStream inputStream, OutputStream outputStream)
            throws IOException
    {
        byte[] buffer = new byte[1000];
        int length;

        while ((length = inputStream.read(buffer)) > 0)
        {
            outputStream.write(buffer, 0, length);
        }
    }
}

Also another way of using Zlib is indeed with the Inflater and Deflater classes:

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.zip.Deflater;
import java.util.zip.Inflater;

import static java.util.zip.Deflater.BEST_COMPRESSION;

public class CompressionUtils
{
    private static final int BUFFER_SIZE = 1024;

    public static byte[] compress(final byte[] data) throws IOException
    {
        final Deflater deflater = new Deflater();
        deflater.setLevel(BEST_COMPRESSION);
        deflater.setInput(data);

        try (final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length))
        {
            deflater.finish();
            final byte[] buffer = new byte[BUFFER_SIZE];
            while (!deflater.finished())
            {
                final int count = deflater.deflate(buffer);
                outputStream.write(buffer, 0, count);
            }

            return outputStream.toByteArray();
        }
    }

    public static byte[] decompress(final byte[] data) throws Exception
    {
        final Inflater inflater = new Inflater();
        inflater.setInput(data);

        try (final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length))
        {
            byte[] buffer = new byte[BUFFER_SIZE];
            while (!inflater.finished())
            {
                final int count = inflater.inflate(buffer);
                outputStream.write(buffer, 0, count);
            }

            return outputStream.toByteArray();
        }
    }
}
Centesimo answered 28/5, 2016 at 12:23 Comment(4)
What is the goal of the scanner? Just getting the first line of the stream?Ruggiero
@Paŭlo Ebermann: This is a trick to read the whole stream at onceCentesimo
Why are Buffered(In|Out)putStream absent?Aldus
@Stephan: For no particular reason, the code works fine. Maybe it would be faster for bigger files to include the buffered versions thoughCentesimo

© 2022 - 2024 — McMap. All rights reserved.