5

I'm using the zlib.NET library to try and inflate files that are compressed by zlib (on a Linux box, perhaps). Here's what I'm doing:

zlib.ZInputStream zinput =
    new zlib.ZInputStream(File.Open(path, FileMode.Open, FileAccess.Read));

while (stopByte != (data = zinput.ReadByte()))
{
  // check data here
}

zinput.Close();

The data bytes match the compressed data bytes, so I must be doing something wrong.

Ben Collins
  • 20,538
  • 18
  • 127
  • 187
  • 1
    To handle gzip/zlib/deflate compressed files, even if the compressed data starts in the middle of the file, I found the [offzip](http://aluigi.altervista.org/mytoolz.htm#offzip) tool [mentioned on reverseengineering.se](https://reverseengineering.stackexchange.com/questions/1463/are-there-any-tools-or-scripts-for-identifying-compression-algorithms-in-executa) useful. It find and extracts compressed streams regardless where they start in the file. With the right command line parameters it works in if it's a pure deflate stream without a header to mark it. – CodesInChaos Dec 07 '15 at 16:32

6 Answers6

7

Other than failing to use a "using" statement to close the stream even in the face of an exception, that looks okay to me. Is the data definitely compressed? Are you able to decompress it with zlib on the linux box?

Having looked at the source code, it's pretty ghastly - a call to int Read(buffer, offset, length) will end up calling its internal int Read() method length times for example. Given that sort of shaky start, I'm not sure I'd trust the code particularly heavily, but I'd have expected it to work at least slightly! Have you tried using SharpZipLib?

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 1
    I don't think SharpZipLib does zlib, but DotNetZip does: http://dotnetzip.codeplex.com/ –  Mar 24 '11 at 02:32
7

It appears I made the mistake of assuming all virtual methods were overridden, which wasn't the case. I was using zlib.ZInputStream.ReadByte(), which is just the inherited Stream.ReadByte(), which doesn't do any inflate.

I used zlib.ZInputStream.Read() instead, and it worked like it should.

Ben Collins
  • 20,538
  • 18
  • 127
  • 187
  • 3
    That sounds like a bad implementation of Stream, as the base ReadByte() will call Read() to read exactly one byte. – Dave Van den Eynde Aug 27 '10 at 12:28
  • 1
    yeah - actually, it doesn't override `Read` either, instead implementing `read`. It also overrides BinaryReader, not Stream, which is goofy. I have since switched to using the DotNetZip library's `Ionic.Zlib` namespace, which has a proper `Stream`-based implementation. zlib.NET just isn't suitable for production use, and is really just a teaser for a paid product anyway. – Ben Collins Aug 27 '10 at 14:46
  • 2
    Just hit this same issue. Am not that impressed with zlib.NET! Why on earth does `ZInputStream` not override `Stream`? Instead, it overrides `BinaryReader`, and incompletely at that! I don't like how all solutions here seem to suggest copying the deflated stream into memory as well. I'll try Ionic.ZLib as you suggest. Thanks. – Drew Noakes Jul 22 '13 at 03:36
6

Skipping the zlib header (first two bytes, 78 9C) and then using the DeflateStream built into .net worked for me.

using(var input = File.OpenRead(...))
using(var output = File.Create(...))
{
    // if there are additional headers before the zlib header, you can skip them:
    // input.Seek(xxx, SeekOrigin.Current);

    if (input.ReadByte() != 0x78 || input.ReadByte() != 0x9C)//zlib header
        throw new Exception("Incorrect zlib header");

    using (var deflateStream = new DeflateStream(decryptedData, CompressionMode.Decompress, true))
    {
        deflateStream.CopyTo(output);
    }
}
CodesInChaos
  • 106,488
  • 23
  • 218
  • 262
  • Symptoms of this zlip deflate file header: the file starts with a lowercase ```x``` or, if you are in base64, starts with ```eJ``` – FremyCompany Feb 16 '16 at 21:58
  • 1
    Take care, the second byte is a flag for compression [as described here](https://stackoverflow.com/a/30794147): `0x01` - No Compression/low, `0x9C` - Default Compression, `0xDA` - Best Compression – Sven Jul 09 '19 at 07:07
2

I recently had the misfortune of serving out docs previously zlib'd using php to a variety of browsers and platforms including IE7. Once I figured out that the docs were zlib'd and not gzip'd (as was thought at the time) I used SharpZipLib in the following way in .NET Framework v4 (taking advantage of Stream.CopyTo):

public static byte[] DecompressZlib(Stream source)
{
    byte[] result = null;
    using (MemoryStream outStream = new MemoryStream())
    {
        using (InflaterInputStream inf = new InflaterInputStream(source))
        {
            inf.CopyTo(outStream);
        }
        result = outStream.ToArray();
    }
    return result;
}

Thought I would put it here in case anyone needs help with the classes to use from SharpZipLib.

Scotty.NET
  • 12,533
  • 4
  • 42
  • 51
2

The below code could help you guys. Instantiate the object and make use of the functions.

public class FileCompressionUtility
{
    public FileCompressionUtility()
    {
    }

    public static void CopyStream(System.IO.Stream input, System.IO.Stream output)
    {
        byte[] buffer = new byte[2000];
        int len;
        while ((len = input.Read(buffer, 0, 2000)) > 0)
        {
            output.Write(buffer, 0, len);
        }
        output.Flush();
    }

    public void compressFile(string inFile, string outFile)
    {
        System.IO.FileStream outFileStream = new System.IO.FileStream(outFile, System.IO.FileMode.Create);
        zlib.ZOutputStream outZStream = new zlib.ZOutputStream(outFileStream, zlib.zlibConst.Z_DEFAULT_COMPRESSION);
        System.IO.FileStream inFileStream = new System.IO.FileStream(inFile, System.IO.FileMode.Open);
        try
        {
            CopyStream(inFileStream, outZStream);
        }
        finally
        {
            outZStream.Close();
            outFileStream.Close();
            inFileStream.Close();
        }
    }

    public void uncompressFile(string inFile, string outFile)
    {
        int data = 0;
        int stopByte = -1;
        System.IO.FileStream outFileStream = new System.IO.FileStream(outFile, System.IO.FileMode.Create);
        zlib.ZInputStream inZStream = new zlib.ZInputStream(System.IO.File.Open(inFile, System.IO.FileMode.Open, System.IO.FileAccess.Read));
        while (stopByte != (data = inZStream.Read()))
        {
            byte _dataByte = (byte)data;
            outFileStream.WriteByte(_dataByte);
        }

        inZStream.Close();
        outFileStream.Close();
    }
}
Niels van der Rest
  • 31,664
  • 16
  • 80
  • 86
0

Look at the sample code more closely, it is copying data from a regular Filestream to the ZOutputStream. The decompression must be happening through that layer.

private void decompressFile(string inFile, string outFile)
{
    System.IO.FileStream outFileStream = new System.IO.FileStream(outFile, System.IO.FileMode.Create);
    zlib.ZOutputStream outZStream = new zlib.ZOutputStream(outFileStream);
    System.IO.FileStream inFileStream = new System.IO.FileStream(inFile, System.IO.FileMode.Open);          
    try
    {
        CopyStream(inFileStream, outZStream);
    }
    finally
    {
        outZStream.Close();
        outFileStream.Close();
        inFileStream.Close();
    }
}
Brendan Kowitz
  • 1,795
  • 10
  • 14
  • You're meant to be able to compress/decompress on either input or output though. ZInputStream definitely has the ability to compress or decompress. – Jon Skeet Oct 09 '08 at 05:20