将GZip压缩应用于提取数据的方法所使用的流

时间:2018-03-27 17:38:31

标签: c# stream compression gzip

先决条件

我有一个流rawStream和一个获取流并将其读到最后的方法,让我们这样说:

Task UploadFile(Stream stream) { ... }

目前,此方法已成功使用,如下所示:

await UploadFile(rawStream);

我想做什么

现在我需要将GZip压缩应用于该流。我希望我能写出这样的东西:

using (var compressedStream = new GZipStream(rawStream, CompressionLevel.Fastest))
{
    await UploadFile(compressedStream);
}

但是这不起作用,因为stream的{​​{1}}参数是一个输出流,所以它的方法是错误的。

问题

如何将原始流包装在压缩流中,仍然让我的消费功能将数据从流中拉出来?

注意

上面的例子实际上是简单的,因为我还需要应用base64编码。所以我真正想要的是这样的:

GZipStream

但我想如果有人能向我解释压缩部分是如何工作的,我可以弄清楚如何实现整个链。

2 个答案:

答案 0 :(得分:0)

您需要第三个流来GZipStream写入:

private static async Task<MemoryStream> CompressStream(Stream inputStream)
{
    var outputStream = new MemoryStream();
    Console.WriteLine(inputStream.Length);
    using (var gzipStream = new GZipStream(outputStream, CompressionLevel.Fastest, leaveOpen: true))
    {
        await inputStream.CopyToAsync(gzipStream);
    }

    Console.WriteLine(outputStream.Length);
    return outputStream;
}

答案 1 :(得分:0)

到目前为止,所提出的方法都没有让我信服。所以我继续编写了以下自定义流,允许从流中提取GZipped和Base64编码数据。

我做了一些测试,似乎工作正常。

我认为这种模式在其他环境中也很有用,可以将“推送管道”转变为“拉动管道”。

public sealed class GzipBase64Stream : Stream
{

    #region constructor / cleanup

    public GzipBase64Stream(Stream inputStream)
    {
        try
        {
            InputStream = inputStream;
            ToBase64Transform = new ToBase64Transform();
            OutputStream = new MemoryStream();
            Base64Stream = new CryptoStream(OutputStream, ToBase64Transform, CryptoStreamMode.Write);
            GzipStream = new GZipStream(Base64Stream, CompressionLevel.Fastest, true);
        }
        catch
        {
            Cleanup();
            throw;
        }
    }

    private void Cleanup()
    {
        GzipStream?.Dispose();
        Base64Stream?.Dispose();
        OutputStream?.Dispose();
        ToBase64Transform?.Dispose();
        InputStream?.Dispose();
    }

    #endregion

    #region private variables

    private bool EndOfInputStreamReached = false;

    private readonly Stream InputStream;
    private readonly ToBase64Transform ToBase64Transform;
    private readonly MemoryStream OutputStream;
    private readonly CryptoStream Base64Stream;
    private readonly GZipStream GzipStream;

    #endregion

    #region stream overrides

    public override bool CanRead => true;

    public override bool CanSeek => false;

    public override bool CanWrite => false;

    public override long Length => 0;

    public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); }

    public override void SetLength(long value) => throw new NotSupportedException();

    public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();

    public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException();

    public override void Flush() => throw new NotSupportedException();

    public override int Read(byte[] buffer, int offset, int count)
    {

        while ((OutputStream.Position >= (OutputStream.Length - 1)) && !EndOfInputStreamReached)
        {
            // No unread data available in the output buffer
            // -> release memory of output buffer and read new data from the source and feed through the pipeline
            OutputStream.SetLength(0);
            var inputBuffer = new byte[1024];
            var readCount = InputStream.Read(inputBuffer, 0, inputBuffer.Length);
            if (readCount == 0)
            {
                EndOfInputStreamReached = true;
                GzipStream.Flush();
                GzipStream.Dispose(); // because Flush() does not actually flush...
            }
            else
            {

                GzipStream.Write(inputBuffer, 0, readCount);
            }
            OutputStream.Position = 0;
        }

        return OutputStream.Read(buffer, offset, count);
    }

    protected override void Dispose(bool disposing)
    {
        base.Dispose(disposing);
        if (disposing)
            Cleanup();
    }

    #endregion

}