到目前为止,没有一种方法能真正说服我。所以我继续写了下面的定制流,它允许从流中提取gzip和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
}