Search code examples
c#compressiondotnet-httpclientsharpziplibicsharpcode

How to compress file data with ICSharpCode.SharpZipLib and upload using HttpClient?


I'm using ICSharpCode.SharpZipLib and doing the following: reading a file content, compressing (zlib) and then uploading to a server using HttpClient.PostAsync

My regular approach (as works for corresponding download) would be to create a compressed outputstream and then do Copy/CopyAsync from output to to a StreamContent. However, DeflaterOutputStream does not support reading, so this won't work. How should it be done effectively? It might just be only me, but the answer is not obvious.

I'm aware that I could compress file into memory first (ie. MemoryStream or byte array), and then copy those to http content, but that's not acceptable


Solution

  • The solution was to implement HttpContent abstract class, which will give access to http body's output stream. It is important to note, that DeflaterOutputStream will close its outputstream on dispose, we don't want that, so have to set IsStreamOwner to false.

    using System.IO;
    using System.Net;
    using System.Net.Http;
    using System.Threading.Tasks;
    using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
    
    public class ZlibContent : HttpContent
    {
        private readonly Stream _source;
    
        public ZlibContent(Stream _source)
        {
            _source = source;
        }
    
        protected override async Task SerializeToStreamAsync(Stream destinationStream, TransportContext context)
        {
            using (var zlibStream = new DeflaterOutputStream(destinationStream) { IsStreamOwner = false })
            {
                await _source.CopyAsync(zlibStream, this.progress);
            }
        }
    
        protected override bool TryComputeLength(out long length)
        {
            length = 0;
            return false;
        }
    }
    

    So the use it a following way:

    using (var fileStream = /* open file stream */)
    using (var content = new ZlibContent(fileStream))
    {
        await httpClient.PostAsync("url", content);
    }
    

    So the key point is when compress-stream (DeflaterOutputStream) is in the "middle", one does not copy from it, but copies to it.