Paolo Ardissone
Paolo Ardissone

Reputation: 847

GZip/Deflate compression works for .NetCore 2.0 but not for .Net 4.6.2

I have written some code for compressing and decompressing data with Gzip or Deflate (both inside System.IO.Compression) and I have tested it in a .NetCore project where it works fine and all test cases pass. So I've recycled the same code in a .Net 4.6.2 project where I'm having issues to make it work despite the msdn documentation saying that it's compatible. Here you are the portion of code I'm havign issues with

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();
            }
        }
    }

The GenerateEncondingStream simply call the constructor of DeflateStream or Gzipstream, depending on the ones you choose.

ISSUE: If I use Deflate, msResult.Lenght is 0 cells, while if I use GZip Length msResult.Length is 10 cells for the same dataset (both 211 in the .NetCore project).

The massive use of Flush() is due to make sure that every stream is completely flushed where it should be. Are there problems with the .Net 4.6.2 or it's wrong my code? Thanks for the help!

EDIT: Same problem, but with decompression and deserialization: if I decompress and then I deserialized after closing all the streams, I have a 10 % of the performance than the version listed below which uses incapsulated streams. This code below fails on deserializing while is decompressing. I'm out of ideas, because the version with 10% of efficency is not acceptable...

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);

                        }
                    }
                }
            }
        }
    }

Upvotes: 2

Views: 1806

Answers (1)

Marc Gravell
Marc Gravell

Reputation: 1064274

In the reference source of DeflateStream, Flush() doesn't do anything.

Looking in reflector, this is true of the regular .NET Framework Flush() too. So basically, Flush() doesn't want to flush - presumably to maximise compression effectiveness. Instead, flushing only happens when the data size is large enough, or when it is disposed.

By contrast, in the .NET Core source, Flush() actually flushes.

So: move your .ToArray() calls to after you've closed everything that wraps the MemoryStream.

So:

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();
}

Upvotes: 4

Related Questions