Contained file missing extension when using GZipStream

Aug 8, 2011 at 11:20 PM
Edited Aug 8, 2011 at 11:29 PM

When using the Ionic.Zlib.GZipStream class to compress a file, the extension is missing on the contained file withing the newly created GZip file.

The below example is based on sample code in the GZipStream(Stream, CompressionMode) constructor documentation.  After creating the GZip with the below code open the resulting gzip file in an external tool like WinZip or 7Zip you will see the extension is missing.  I would like to know what is missing from the example to allow the extension to be properly set on the contained file.

static void Main(string[] args)
    const int WORKING_BUFFER_SIZE = 4096;
    string fileToCompress = Path.Combine(Environment.GetEnvironmentVariable("windir"), "notepad.exe");
    const string outputFile = @"c:\notepad.gzip";

    using (Stream input = File.OpenRead(fileToCompress))
    using (Stream raw = File.Create(outputFile))
    using (GZipStream compressor = new GZipStream(raw, CompressionMode.Compress))
        byte[] buffer = new byte[WORKING_BUFFER_SIZE];
        int n;
        while ((n = input.Read(buffer, 0, buffer.Length)) != 0)
            compressor.Write(buffer, 0, n);

Aug 8, 2011 at 11:35 PM

I noticed that if I set the GZipStream.FileName property it will comeout whatever I set it to. The default is null. In a simple example this seems like no big deal and an easy solution to just set this.

However in a real word example when only the Stream's are available this is not possible as far as I know.  I would have to refactor the code to include the file name for the contained file to go with the input stream as it "moves" through the system.

It seems that the extension is part of the FileStream that was created somehow, and it is being stripped when being included in the GZip. I could be wrong and this is just an artifact of detailing with streams.

Any help on the topic would be appreciated. Thanks.

Aug 9, 2011 at 12:29 AM
Edited Aug 9, 2011 at 12:33 AM

Currently, the filename is not stored unless you set the FileName property.

There's no way for GZipStream to know the name of the file that your input stream refers to, or even that the input stream is from a file.   I suppose that 7zip and winzip are inferring the name of the compressed image inside the .gzip file from the name of the gzip file itself, which is "notepad.gzip".  The normal convention on a gzipped file is to append (not replace) the extension .gz to the original filename.  Therefore "Readme.txt" becomes "Readme.txt.gz" after gzipping.  But this is a convention - it is not specified or recommended in the GZIP standard.  So it is up to your app to set the name of the output file that receives the gzipped data. It could be that 7zip and winzip are simply liberalizing that convention, and stripping the complete .gzip extension, leaving you with.... "notepad".   You can easily test it further to verify this.  Rename the gzip file to "" and see what 7z thinks is inside that file.  I know which way I am betting.

As for the "real world" - no.... there is no way for the GZipStream to "know" the filename used for its input.  Look at your code: the GZipStream has no direct contact with the input stream.  The only connection is the byte array that is read from the input, then written into the GZipStream.  So there is no way for GZipStream to infer the filename.  You must set it explicitly.

On the FileStream instance, there is a Name property.  You can use THAT as the FileName for the GZipStream.  You would need a line like this in your code:

compressor.FileName = input.Name;


Aug 9, 2011 at 12:40 AM

Thanks, I forgot about all that GZip convention stuff.  Thanks for the reminder.