landings
landings

Reputation: 770

What is the simplest way to decompress a ZIP buffer in C#?

When I use zlib in C/C++, I have a simple method uncompress which only requires two buffers and no more else. Its definition is like this:

int uncompress (Bytef *dest, uLongf *destLen, const Bytef *source,
uLong sourceLen); 
/*
Decompresses the source buffer into the destination buffer.  sourceLen is    the byte length of the source buffer.  Upon entry,
destLen is the total size    of the destination buffer, which must be
large enough to hold the entire    uncompressed data.  (The size of
the uncompressed data must have been saved    previously by the
compressor and transmitted to the decompressor by some    mechanism
outside the scope of this compression library.) Upon exit, destLen   
is the actual size of the uncompressed data.

uncompress returns Z_OK if success, Z_MEM_ERROR if there was not    enough memory, Z_BUF_ERROR if there was not enough room in the output 
buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete.
In    the case where there is not enough room, uncompress() will fill
the output    buffer with the uncompressed data up to that point.
*/

I want to know if C# has a similar way. I checked SharpZipLib FAQ as follows but did not quite understand:

How do I compress/decompress files in memory?

Use a memory stream when creating the Zip stream!

MemoryStream outputMemStream = new MemoryStream();
using (ZipOutputStream zipOutput = new ZipOutputStream(outputMemStream)) {   
// Use zipOutput stream as normal    
...  

You can get the resulting data with memory stream methods ToArray or GetBuffer.

ToArray is the cleaner and easiest to use correctly with the penalty of duplicating allocated memory. GetBuffer returns a raw buffer raw and so you need to account for the true length yourself.

See the framework class library help for more information.

I can't figure out if this block of code is for compression or decompression, if outputMemStream meas a compressed stream or an uncompressed stream. I really hope there is a easy-to-understand-way like in zlib. Thanks you very much if you can help me.

Upvotes: 1

Views: 2675

Answers (1)

vinicius.ras
vinicius.ras

Reputation: 1596

Check out the ZipArchive class, which I think has the features you need to accomplish in-memory decompression of zip files.

Assuming you have an array of bytes (byte []) which represent the ZIP file in memory, you have to instantiate a ZipArchive object which will be used to read that array of bytes and interpret them as the ZIP file you whish to load. If you check the ZipArchive class' available constructors in documentation, you will see that they require a stream object from which the data will be read. So, first step would be to convert your byte [] array to a stream that can be read by the constructors, and you can do this by using a MemoryStream object.

Here's an example of how to list all entries inside of a ZIP archive represented in memory as a bytes array:

byte [] zipArchiveBytes = ...;   // Read the ZIP file in memory as an array of bytes

using (var inputStream = new MemoryStream(zipArchiveBytes))
using (var zipArchive = new ZipArchive(inputStream, ZipArchiveMode.Read))
{
    Console.WriteLine("Listing archive entries...");
    foreach (var archiveEntry in zipArchive.Entries)
        Console.WriteLine($"   {archiveEntry.FullName}");
}

Each file in the ZIP archive will be represented as a ZipArchiveEntry instance. This class offers properties which allow you to retrieve information such as the original length of a file from the ZIP archive, its compressed length, its name, etc.

In order to read a specific file which is contained inside the ZIP file, you can use ZipArchiveEntry.Open(). The following exemplifies how to open a specific file from an archive, if you have its FullName inside the ZIP archive:

ZipArchiveEntry archEntry = zipArchive.GetEntry("my-folder-inside-zip/dog-picture.jpg");
byte[] readResult;
using (Stream entryReadStream = archEntry.Open())
{
    using (var tempMemStream = new MemoryStream())
    {
        entryReadStream.CopyTo(tempMemStream);
        readResult = tempMemStream.ToArray();
    }
}

This example reads the given file contents, and returns them as an array of bytes (stored in the byte[] readResult variable) which you can then use according to your needs.

Upvotes: 4

Related Questions