.NET GZipStream解压缩生成空流

时间:2012-08-11 14:41:01

标签: c# .net wpf compression gzipstream

我正在尝试序列化和压缩WPF FlowDocument,然后反向 - 解压缩字节数组并反序列化以重新创建FlowDocument - 使用.NET GZipStream类。我正在关注MSDN上描述的示例,我有以下测试程序:

var flowDocumentIn = new FlowDocument();
flowDocumentIn.Blocks.Add(new Paragraph(new Run("Hello")));
Debug.WriteLine("Compress");
byte[] compressedData;
using (var uncompressed = new MemoryStream())
{
    XamlWriter.Save(flowDocumentIn, uncompressed);
    uncompressed.Position = 0;
    using (var compressed = new MemoryStream())
    using (var compressor = new GZipStream(compressed, CompressionMode.Compress))
    {
        Debug.WriteLine(" uncompressed.Length: " + uncompressed.Length);
        uncompressed.CopyTo(compressor);
        Debug.WriteLine(" compressed.Length: " + compressed.Length);
        compressedData = compressed.ToArray();
    }
}

Debug.WriteLine("Decompress");
FlowDocument flowDocumentOut;
using (var compressed = new MemoryStream(compressedData))
using (var uncompressed = new MemoryStream())
using (var decompressor = new GZipStream(compressed, CompressionMode.Decompress))
{
    Debug.WriteLine(" compressed.Length: " + compressed.Length);
    decompressor.CopyTo(uncompressed);
    Debug.WriteLine(" uncompressed.Length: " + uncompressed.Length);
    flowDocumentOut = (FlowDocument) XamlReader.Load(uncompressed);
}

Assert.AreEqual(flowDocumentIn, flowDocumentOut);

但是我在XamlReader.Load行得到一个异常,这是正常的,因为调试输出告诉未压缩的流的长度为零。

Compress
 uncompressed.Length: 123
 compressed.Length: 202
Decompress
 compressed.Length: 202
 uncompressed.Length: 0

为什么最终的uncompressed流不包含原始的123字节?

(请忽略“压缩”字节数组大于“未压缩”字节数组的事实 - 我通常会使用更大的流文档)

3 个答案:

答案 0 :(得分:11)

在从内存流中获取压缩字节之前,需要关闭GZipStream。在这种情况下,由于使用而调用Dispose处理结束。

using (var compressed = new MemoryStream())
{
    using (var compressor = new GZipStream(compressed, CompressionMode.Compress))
    {
        uncompressed.CopyTo(compressor);
    }
    // Get the compressed bytes only after closing the GZipStream
    compressedBytes = compressed.ToArray();
}

这有效,您甚至可以删除using的{​​{1}},因为它将由MemoryStream处理,除非您使用允许您指定基础流的构造函数重载应该保持开放。这意味着使用该代码在被处理的流上调用GZipStream,但这是允许的,因为字节仍然可用,这使得处理内存流有点奇怪,但如果你不这样做,FXCop会惹恼你。

答案 1 :(得分:3)

Joao的答案奏效了。我复制了下面的完整工作示例。我在输出compressedData.Length中添加了一行。有趣的是,这输出218个字节,而compressedStream.Length仅输出202个字节。如果在读取字节数组之前没有关闭GZipStream,那么compressedData.Length就是202.我不确定为什么关闭GZipStream会给你额外的16个字节..

var flowDocumentIn = new FlowDocument();
flowDocumentIn.Blocks.Add(new Paragraph(new Run("Hello")));

Debug.WriteLine("Compress");

byte[] compressedData;

using (var uncompressedStream = new MemoryStream())
{
    XamlWriter.Save(flowDocumentIn, uncompressedStream);
    uncompressedStream.Position = 0;
    using (var compressedStream = new MemoryStream())
    {
        using (var gZipCompressor = new GZipStream(compressedStream, CompressionMode.Compress))
        {
            Debug.WriteLine(" uncompressedStream.Length: " + uncompressedStream.Length);
            uncompressedStream.CopyTo(gZipCompressor);
            Debug.WriteLine(" compressedStream.Length: " + compressedStream.Length);
        }
        compressedData = compressedStream.ToArray();
    }
}

Debug.WriteLine(" compressedData.Length: " + compressedData.Length);

Debug.WriteLine("Decompress");

FlowDocument flowDocumentOut;

using (var compressedStream = new MemoryStream(compressedData))
using (var uncompressedStream = new MemoryStream())
{
    using (var gZipDecompressor = new GZipStream(compressedStream, CompressionMode.Decompress))
    {
        Debug.WriteLine(" compressedStream.Length: " + compressedStream.Length);
        gZipDecompressor.CopyTo(uncompressedStream);
        Debug.WriteLine(" uncompressedStream.Length: " + uncompressedStream.Length);
    }
    uncompressedStream.Position = 0;
    flowDocumentOut = (FlowDocument)XamlReader.Load(uncompressedStream);
}

调试输出:

Compress
 uncompressedStream.Length: 123
 compressedStream.Length: 202
 compressedData.Length: 218
Decompress
 compressedStream.Length: 218
 uncompressedStream.Length: 123

在致电uncompressedStream.Position = 0;之前,请注意额外的XamlReader.Load

答案 2 :(得分:0)

将解压缩的字节复制到流后,需要将其位置设置为零,以便正确读取