Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/304.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# .NET Gzip流解压缩生成空流_C#_.net_Wpf_Compression_Gzipstream - Fatal编程技术网

C# .NET Gzip流解压缩生成空流

C# .NET Gzip流解压缩生成空流,c#,.net,wpf,compression,gzipstream,C#,.net,Wpf,Compression,Gzipstream,我正在尝试序列化和压缩WPF,然后使用.NET类执行相反的操作—解压缩字节数组并反序列化以重新创建FlowDocument。我遵循MSDN上描述的示例,我有以下测试程序: var flowDocumentIn = new FlowDocument(); flowDocumentIn.Blocks.Add(new Paragraph(new Run("Hello"))); Debug.WriteLine("Compress"); byte[] compressedData; using (var

我正在尝试序列化和压缩WPF,然后使用.NET类执行相反的操作—解压缩字节数组并反序列化以重新创建FlowDocument。我遵循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
为什么最终的
未压缩的
流不包含原始的123字节


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

在从内存流获取压缩字节之前,需要关闭
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();
}

这是可行的,您甚至可以使用
MemoryStream
删除
,因为它将由
GZipStream
处理,除非您使用构造函数重载来指定底层流应该保持打开状态。这意味着您正在对已处理的流调用
ToArray
,但这是允许的,因为字节仍然可用,这使得处理内存流有点奇怪,但如果不这样做,FXCop会让您感到恼火。

Joao的回答起到了作用。我复制了下面完整的工作示例。我在输出
压缩数据.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在调用<代码> XAMLReald.Load  ./P>> P>在将解压缩后的字节复制到流中之后,需要将其位置设置为0,以便可以正确读取

。尽管您可以解决此问题,但首先应该考虑是否使用该类。请参阅我的评论:“在从内存流获取压缩字节之前,需要关闭
GZipStream
”—为什么?如果在关闭之前调用
.ToArray()
和关闭之后调用
.ToArray()
为什么会得到不同的字节数?除了输出是以块的形式写入之外,
GZipStream
在压缩数据之前添加一个页眉,在压缩数据之后添加一个页脚。页脚只能在关闭流时添加。deflate压缩器(根据gzip格式的要求)以块的形式生成压缩输出。压缩器需要累积数据以建立块并在其发出之前生成统计信息。当您到达输入的末尾时,您需要告诉平减指数完成最后一个块并将其发送出去。另外,压缩数据大小超过未压缩数据大小的量是GZipStream中的几个错误之一。