在dotNET C#中使用GZipStream的正确方法

时间:2016-01-03 08:21:45

标签: c# .net .net-3.5 gzipstream

我目前正在使用.net 3.5与GZipStream合作。 我有两种方法,如下所示。作为输入文件,我使用由chars'组成的文本文件。文件大小为2MB。如果我使用.net 4.5,但是在压缩和解压缩后使用.net 3.5,这个代码工作正常我得到大小为435KB的文件,这当然与源文件不一样。 如果我尝试通过WinRAR解压缩文件,它看起来也很好(与源文件相同)。 如果我尝试使用.net4.5中的GZipStream解压缩文件(通过.net 3.5通过GZipStream压缩的文件),结果很糟糕。

UPD: 一般来说,我真的需要将文件作为几个单独的gzip块读取,在这种情况下,一次调用Read()方法就会读取压缩文件的所有字节,所以我仍然不明白为什么解压缩没有#n;'工作。

    public void CompressFile()
    {
        string fileIn = @"D:\sin2.txt";
        string fileOut = @"D:\sin2.txt.pgz";

        using (var fout = File.Create(fileOut))
        {
            using (var fin = File.OpenRead(fileIn))
            {
                using (var zip = new GZipStream(fout, CompressionMode.Compress))
                {
                    var buffer = new byte[1024 * 1024 * 10];
                    int n = fin.Read(buffer, 0, buffer.Length);
                    zip.Write(buffer, 0, n);
                }
            }
        }
    }
    public void DecompressFile()
    {
        string fileIn = @"D:\sin2.txt.pgz";
        string fileOut = @"D:\sin2.1.txt";

        using (var fsout = File.Create(fileOut))
        {
            using (var fsIn = File.OpenRead(fileIn))
            {
                var buffer = new byte[1024 * 1024 * 10];
                int n;
                while ((n = fsIn.Read(buffer, 0, buffer.Length)) > 0)
                {
                    using (var ms = new MemoryStream(buffer, 0, n))
                    {
                        using (var zip = new GZipStream(ms, CompressionMode.Decompress))
                        {
                            int nRead = zip.Read(buffer, 0, buffer.Length);
                            fsout.Write(buffer, 0, nRead);
                        }
                    }
                }
            }
        }
    }

2 个答案:

答案 0 :(得分:2)

您正在尝试解压缩每个“块”,就像它是一个单独的gzip文件一样。不要这样做 - 只需从循环中的GZipStream读取:

using (var fsout = File.Create(fileOut))
{
    using (var fsIn = File.OpenRead(fileIn))
    {
        using (var zip = new GZipStream(fsIn, CompressionMode.Decompress))
        {
            var buffer = new byte[1024 * 32];
            int bytesRead;

            while ((bytesRead = zip.Read(buffer, 0, buffer.Length)) > 0)
            {
                fsout.Write(buffer, 0, bytesRead);
            }
        }
    }
}

请注意,您的压缩代码看起来应该相似,在循环中读取而不是假设对Read的单个调用将读取所有数据。

(我个人会跳过fsIn,只使用new GZipStream(File.OpenRead(fileIn)),但这只是个人偏好。)

答案 1 :(得分:1)

首先,正如@Jon Skeet所提到的,您没有正确使用Stream.Read方法。如果你的缓冲区足够大或者不重要,那么流不允许返回比请求更少的字节,零指示不再有,所以从流中读取应始终在循环中执行。

但是,解压缩代码中的主要问题是共享缓冲区的方式。您将输入读入缓冲区,而不是包含在MemoryStream中(请注意,使用的构造函数不会复制传递的数组,但实际上将其设置为' s内部缓冲区),然后您尝试同时读取和写入该缓冲区。考虑到解压缩写入数据"更快"比起阅读,令人惊讶的是你的代码完全可以运行。

正确的实现非常简单

static void CompressFile()
{
    string fileIn = @"D:\sin2.txt";
    string fileOut = @"D:\sin2.txt.pgz";
    using (var input = File.OpenRead(fileIn))
    using (var output = new GZipStream(File.Create(fileOut), CompressionMode.Compress))
        Write(input, output);
}

static void DecompressFile()
{
    string fileIn = @"D:\sin2.txt.pgz";
    string fileOut = @"D:\sin2.1.txt";
    using (var input = new GZipStream(File.OpenRead(fileIn), CompressionMode.Decompress))
    using (var output = File.Create(fileOut))
        Write(input, output);
}

static void Write(Stream input, Stream output, int bufferSize = 10 * 1024 * 1024)
{
    var buffer = new byte[bufferSize];
    for (int readCount; (readCount = input.Read(buffer, 0, buffer.Length)) > 0;)
        output.Write(buffer, 0, readCount);
}