【问题标题】:GZip/Deflate compression works for .NetCore 2.0 but not for .Net 4.6.2GZip/Deflate 压缩适用于 .NetCore 2.0,但不适用于 .Net 4.6.2
【发布时间】:2018-07-15 10:02:25
【问题描述】:

我已经编写了一些代码,用于使用 Gzip 或 Deflate(都在 System.IO.Compression 中)压缩和解压缩数据,并且我在一个 .NetCore 项目中对其进行了测试,它工作正常并且所有测试用例都通过了。因此,我在 .Net 4.6.2 项目中回收了相同的代码,尽管 msdn 文档说它是兼容的,但我仍然无法使其正常工作。这是我遇到问题的代码部分

public virtual byte[] Encode<TObject>(TObject objectToEncode) where TObject : class, new()
    {

        if (objectToEncode == null) throw new ArgumentNullException(nameof(objectToEncode));

        byte[] jsonResult = null;
        using (var ms = new MemoryStream())
        {

            //Generazione del Json
            using (var textWriter = new StreamWriter(ms, System.Text.Encoding.UTF8))
            {
                using (var jsonWriter = new JsonTextWriter(textWriter))
                {
                    JsonSerializer serializer = new JsonSerializer();
                    serializer.Serialize(jsonWriter, objectToEncode, objectToEncode.GetType());
                    jsonWriter.Flush();
                    textWriter.Flush();
                    ms.Flush();
                    //Compressione Json   
                    jsonResult = ms.ToArray();
                }
            }
        }
        using (var msResult = new MemoryStream())
        {
            using (var encodingStream = GenerateEncodingStream(msResult))
            {
                encodingStream.Write(jsonResult, 0, Convert.ToInt32(jsonResult.Length));
                encodingStream.Flush();
                msResult.Position = 0;
                return msResult.ToArray();
            }
        }
    }

GenerateEncondingStream 只需调用 DeflateStream 或 Gzipstream 的构造函数,具体取决于您选择的那些。

问题: 如果我使用 Deflate,msResult.Lenght 是 0 个单元格,而如果我使用 GZip 长度 msResult.Length 是 10 个单元格,用于同一数据集(在 .NetCore 项目中都是 211 个)。

大量使用 Flush() 是为了确保每个流都被完全刷新到应该在的位置。 .Net 4.6.2 有问题还是我的代码有问题?感谢您的帮助!

编辑: 同样的问题,但解压和反序列化:如果我解压缩然后在关闭所有流后反序列化,我的性能比下面列出的使用封装流的版本高 10%。 下面的代码在解压缩时反序列化失败。 我没有想法,因为效率为 10% 的版本是不可接受的……

public virtual async Task<object> DecodeAsync(byte[] encodedObject, Type decodedObjectType, CancellationToken cancellationToken)
    {           
        cancellationToken.ThrowIfCancellationRequested();

        if (encodedObject == null) throw new ArgumentNullException(nameof(encodedObject));

        if (encodedObject.Length == 0) throw new ArgumentException(nameof(encodedObject));

        using (var compressedStream = new MemoryStream(encodedObject))
        {
            using (var csStream = GenerateDecodingStream(compressedStream))
            {
                using (var decompressedStream = new MemoryStream())
                {
                    using (StreamReader sr = new StreamReader(decompressedStream, System.Text.Encoding.UTF8))
                    {
                        using (var reader = new JsonTextReader(sr))
                        {
                            JsonSerializer serializer = new JsonSerializer();
                            return serializer.Deserialize(reader, decodedObjectType);

                        }
                    }
                }
            }
        }
    }

【问题讨论】:

  • “我在让它工作时遇到了问题”——什么种类的问题?你得到意外的输出吗?例外?
  • “如果我使用 Deflate,msResult.Lenght 为 0 个单元格,而如果我使用 GZip 长度 msResult.Length 为 10 个单元格,则同一数据集(在 .NetCore 项目中约为 200 个)。”我很可能应该突出显示此部分
  • 您可以尝试将ToArray() 调用移到 encodingStream / textWriterusing 块之外吗?我想知道它是否只是缓冲 - 即只使用ms.ToArray() 之后你已经完全关闭了装饰MemoryStream的东西
  • 嗯。为什么将MemoryStream 中的位置设置为0?这似乎没用。
  • Flush method does nothing for DeflateStream (因此为GZipStream) - 见备注 "这个方法的当前实现不刷新内部缓冲区。释放对象时会刷新内部缓冲区。".在获取结果之前致电Close

标签: c# .net-core gzip deflate .net-4.6.2


【解决方案1】:

reference source of DeflateStreamFlush()什么都不做

查看反射器,常规 .NET Framework Flush() 也是如此。所以基本上,Flush() 不想冲洗——大概是为了最大限度地提高压缩效率。相反,刷新仅在数据大小足够大或处理时发生。

相比之下,在 .NET Core 源代码中,Flush()actually flushes

所以:将您的.ToArray() 调用移至您已经关闭了包装MemoryStream所有内容

所以:

using (var ms = new MemoryStream())
{

    //Generazione del Json
    using (var textWriter = new StreamWriter(ms, System.Text.Encoding.UTF8))
    {
        using (var jsonWriter = new JsonTextWriter(textWriter))
        {
            JsonSerializer serializer = new JsonSerializer();
            serializer.Serialize(jsonWriter, objectToEncode, objectToEncode.GetType());                    
        }
    }
    jsonResult = ms.ToArray();
}
using (var msResult = new MemoryStream())
{
    using (var encodingStream = GenerateEncodingStream(msResult))
    {
        encodingStream.Write(jsonResult, 0, Convert.ToInt32(jsonResult.Length));                
    }
    return msResult.ToArray();
}

【讨论】:

  • 这个解决方案是正确的,因为 flush() 没有实现,所以你不得不等到流被手动处理或关闭。现在我在解码和反序列化方面遇到了同样的问题,但出于性能目的,我无法将反序列化和解压缩分开。我做了一个编辑,用代码解释了一切。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-03-23
  • 2019-05-30
  • 2016-10-23
  • 2011-06-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多