Search code examples
c#exceptioncompression.net-6.0asp.net-core-6.0

System.IO.InvalidDataException: 'The archive entry was compressed using an unsupported compression method.'


I am using ASP.NET Core 6.0. I have an API which returns compressed data. Simplified version of my code looks like this:

[HttpPost("[action]", Name = "GetData")]
public IActionResult GetData(string request)
{
     string data = GetStaticXMLFileData();
     data = CompressString(data);

     return new ContentResult
     {
         Content = data,
         ContentType = "text/xml",
         StatusCode = (int)HttpStatusCode.OK
     };
}

private string CompressString(string text)
{
    string compressedString;

    using (var memoryStream = new MemoryStream())
    {
        using (var gZipStream = new GZipStream(memoryStream, CompressionMode.Compress, true))
        {
            using (var writer = new StreamWriter(gZipStream, Encoding.UTF8))
            {
                writer.Write(text);
            }

            byte[] compressedData = memoryStream.ToArray();

            compressedString = System.Text.Encoding.UTF8.GetString(compressedData);
        }
    }

    return compressedString;
}

This API endpoint is working well and I have tested this from Swagger.

Now, I am trying to consume them in my client code as under:

private string GetDataAPIResponse()
{
    string httpResponse = string.Empty;

    var urlValue = string.Format("https://localhost:44314/api/CompressedData/GetData?compress=true&result_format=xml");
    var requestUri = new Uri(urlValue);

    var buffer = System.Text.Encoding.UTF8.GetBytes("<data>Test</data>"); //Sample Data Request
    var byteContent = new ByteArrayContent(buffer);

    using (var httpClient = new HttpClient())
    {
        using (var response = httpClient.PostAsync(requestUri, byteContent).GetAwaiter().GetResult())
        {
            response.EnsureSuccessStatusCode();

            if (response.IsSuccessStatusCode && response.StatusCode == HttpStatusCode.OK)
            {
                Task<Stream> task = response.Content.ReadAsStreamAsync();
                task.Wait();
                Stream responseStream = Constants.IsCompressed ?
                                        new GZipStream(task.Result, CompressionMode.Decompress) :
                                        task.Result;

                if (responseStream != null)
                {
                    using (var readStream = new StreamReader(responseStream, Encoding.UTF8))
                        httpResponse = readStream.ReadToEnd(); //Here, I got the Error.
                }
            }
        }

        return httpResponse;
    }
}

I am not able to get the decompressed response using this code.

On this line of code, I am getting an error:

httpResponse = readStream.ReadToEnd();

Error is:

System.IO.InvalidDataException: The archive entry was compressed using an unsupported compression method

Stacktrace:

at System.IO.Compression.Inflater.Inflate(FlushCode flushCode)
at System.IO.Compression.Inflater.ReadInflateOutput(Byte* bufPtr, Int32 length, FlushCode flushCode, Int32& bytesRead)
at System.IO.Compression.Inflater.ReadOutput(Byte* bufPtr, Int32 length, Int32& bytesRead)
at System.IO.Compression.Inflater.InflateVerified(Byte* bufPtr, Int32 length)
at System.IO.Compression.DeflateStream.ReadCore(Span`1 buffer)
at System.IO.Compression.DeflateStream.Read(Byte[] buffer, Int32 offset, Int32 count)
at System.IO.Compression.GZipStream.Read(Byte[] buffer, Int32 offset, Int32 count)
at System.IO.StreamReader.ReadBuffer()
at System.IO.StreamReader.ReadToEnd()

Can someone please suggest me a solution?


Solution

  • Both client and server code are completely wrong. You shouldn't be re-encoding a compressded file back into text. Just pass it as bytes or a stream

    [HttpPost("[action]", Name = "GetData")]
    public IActionResult GetData(string request)
    {
         string data = GetStaticXMLFileData();
         var compressedStream = CompressString(data);
         return new FileStreamResult(compressedStream, "text/xml");
    }
    
    private Stream CompressString(string text)
    {
        var memoryStream = new MemoryStream();
        using (var gZipStream = new GZipStream(memoryStream, CompressionMode.Compress, true))
        using (var writer = new StreamWriter(gZipStream, Encoding.UTF8))
        {
            writer.Write(text);
        }
    
        memoryStream.Position = 0; // seek back to start
        return memoryStream;
    }
    

    And the client shouldn't use .GetResult or .Wait, only await.

    static HttpClient _httpClient = new HttpClient(); // use static or DI client to prevent socket exhaustion
    
    private async Task<string> GetDataAPIResponse()
    {
        var urlValue = $"https://localhost:44314/api/CompressedData/GetData?compress=true&result_format=xml";
        using var content = new StringContent("<data>Test</data>", Encoding.UTF8);
        using var response = await httpClient.PostAsync(urlValue , content);
        response.EnsureSuccessStatusCode();
        using var responseStream = await response.Content.ReadAsStreamAsync();
        using var decompressed = Constants.IsCompressed ?
                                   new GZipStream(responseStream, CompressionMode.Decompress) :
                                   responseStream;
    
        using var reader = new StreamReader(decompressed, Encoding.UTF8);
        var httpResponse = await readStream.ReadToEndAsync();
        return httpResponse;
    }
    

    Having said that, you should probably just use the built-in Automatic Compression/Decompression instead.

    builder.Services.AddResponseCompression(options =>
    {
        options.EnableForHttps = true;
    });
    
    [HttpPost("[action]", Name = "GetData")]
    public IActionResult GetData(string request)
    {
         string data = GetStaticXMLFileData();
         return new ContentResult
         {
             Content = data,
             ContentType = "text/xml",
             StatusCode = (int)HttpStatusCode.OK,
         };
    }
    

    And the client is:

    static HttpClient _httpClient = new HttpClient(new HttpClientHandler
    {
        AutomaticDecompression = DecompressionMethods.All,
    });
    
    private async Task<string> GetDataAPIResponse()
    {
        var urlValue = $"https://localhost:44314/api/CompressedData/GetData?compress=true&result_format=xml";
        using var content = new StringContent("<data>Test</data>", Encoding.UTF8);
        using var response = await httpClient.PostAsync(urlValue , content);
        response.EnsureSuccessStatusCode();
        var httpResponse = await response.Content.ReadAsStringAsync();
        return httpResponse;
    }