Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Uncompress data file with DeflateStream

I'm having trouble reading a compressed (deflated) data file using C# .NET DeflateStream(..., CompressionMode.Decompress). The file was written earlier using DeflateStream(..., CompressionMode.Compress), and it seems to be just fine (I can even decompress it using a Java program).

However, the first Read() call on the input stream to decompress/inflate the compressed data returns a length of zero (end of file).

Here's the main driver, which is used for both compression and decompression:

public void Main(...)
{
    Stream  inp;
    Stream  outp;
    bool    compr;  

    ...
    inp = new FileStream(inName, FileMode.Open, FileAccess.Read);
    outp = new FileStream(outName, FileMode.Create, FileAccess.Write);  

    if (compr)
        Compress(inp, outp);
    else
        Decompress(inp, outp);  

    inp.Close();
    outp.Close();
}

Here's the basic code for decompression, which is what is failing:

public long Decompress(Stream inp, Stream outp)
{
    byte[]  buf = new byte[BUF_SIZE];
    long    nBytes = 0;  

    // Decompress the contents of the input file
    inp = new DeflateStream(inp, CompressionMode.Decompress);  

    for (;;)
    {
        int   len;  

        // Read a data block from the input stream
        len = inp.Read(buf, 0, buf.Length);    //<<FAILS
        if (len <= 0)
            break;  

        // Write the data block to the decompressed output stream
        outp.Write(buf, 0, len);
        nBytes += len;
    }  

    // Done
    outp.Flush();
    return nBytes;
}

The call marked FAILS always returns zero. Why? I know it's got to be something simple, but I'm just not seeing it.

Here's the basic code for compression, which works just fine, and is almost exactly the same as the decompression method with the names swapped:

public long Compress(Stream inp, Stream outp)
{
    byte[]  buf = new byte[BUF_SIZE];
    long    nBytes = 0;  

    // Compress the contents of the input file
    outp = new DeflateStream(outp, CompressionMode.Compress);  

    for (;;)
    {
        int   len;  

        // Read a data block from the input stream
        len = inp.Read(buf, 0, buf.Length);
        if (len <= 0)
            break;  

        // Write the data block to the compressed output stream
        outp.Write(buf, 0, len);
        nBytes += len;
    }  

    // Done
    outp.Flush();
    return nBytes;
}  

Solved

After seeing the correct solution, the constructor statement should be changed to:

inp = new DeflateStream(inp, CompressionMode.Decompress, true);

which keeps the underlying input stream open, and the following line needs to be added following the inp.Flush() call:

inp.Close();

The Close() calls forces the deflater stream to flush its internal buffers. The true flag prevents it from closing the underlying stream, which is closed later in Main(). The same changes should also be made to the Compress() method.

like image 838
David R Tribble Avatar asked Oct 06 '09 22:10

David R Tribble


1 Answers

In your decompress method, are reassigning inp to a new Stream (a deflate stream). You never close that Deflate stream, but you do close the underlying file stream in Main(). A similar thing is going on in the compress method.

I think that the problem is that the underlying file stream is being closed before the deflate stream's finalizers are automatically closing them.

I added 1 line of code to your Decompress and Compress methods: inp.Close() // to the Decompressmehtod

outp.Close() // to the compress method.

a better practice would be to enclose the streams in a using clause.

Here's an alternative way to write your Decompress method (I tested, and it works)


    public static long Decompress(Stream inp, Stream outp)
    {
        byte[]  buf = new byte[BUF_SIZE];
        long    nBytes = 0;  

        // Decompress the contents of the input file
        using (inp = new DeflateStream(inp, CompressionMode.Decompress))
        {
            int len;
            while ((len = inp.Read(buf, 0, buf.Length)) > 0)
            {
                // Write the data block to the decompressed output stream
                outp.Write(buf, 0, len);
                nBytes += len;
            }  
        }
        // Done
        return nBytes;
    }
    
like image 74
JMarsch Avatar answered Oct 16 '22 20:10

JMarsch