BlobClient.OpenReadAsync()返回不可查找流致PGP解密失败如何解决?
问题解答
根因说明
BlobClient.OpenReadAsync()默认返回的是只进网络流,CanSeek属性为false。BouncyCastle的默认PGP解密逻辑需要读取流的尾部元数据、或校验头部格式时会触发Seek操作,因此抛出inputStream must be seek-able错误。
首先明确:解密操作必然需要读取Blob数据,以下方案均不需要预先将整个Blob完整下载到本地磁盘或内存,仅按需拉取所需的Blob片段,符合你的需求。
解决方案
方案1:自定义可查找Blob流包装类
实现继承自Stream的包装类,内部持有BlobClient实例,调用Seek方法时根据目标偏移量发起HTTP范围请求拉取对应位置的Blob数据,无需加载整个文件,可直接兼容原有解密逻辑。
示例实现代码:
public class SeekableBlobStream : Stream { private readonly BlobClient _blobClient; private Stream _currentChunkStream; private long _position; private long? _length; public SeekableBlobStream(BlobClient blobClient) { _blobClient = blobClient; _position = 0; } public override bool CanSeek => true; public override bool CanRead => true; public override bool CanWrite => false; public override long Length { get { if (!_length.HasValue) { _length = _blobClient.GetProperties().Value.ContentLength; } return _length.Value; } } public override long Position { get => _position; set => Seek(value, SeekOrigin.Begin); } public override long Seek(long offset, SeekOrigin origin) { var newPosition = origin switch { SeekOrigin.Begin => offset, SeekOrigin.Current => _position + offset, SeekOrigin.End => Length + offset, _ => throw new ArgumentOutOfRangeException(nameof(origin)) }; if (newPosition != _position) { _currentChunkStream?.Dispose(); _currentChunkStream = null; _position = newPosition; } return _position; } public override int Read(byte[] buffer, int offset, int count) { _currentChunkStream ??= _blobClient.OpenRead(new BlobOpenReadOptions(false) { Position = _position }); var read = _currentChunkStream.Read(buffer, offset, count); _position += read; return read; } public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { _currentChunkStream ??= await _blobClient.OpenReadAsync(new BlobOpenReadOptions(false) { Position = _position }, cancellationToken); var read = await _currentChunkStream.ReadAsync(buffer, offset, count, cancellationToken); _position += read; return read; } public override void Flush() => throw new NotSupportedException(); public override void SetLength(long value) => throw new NotSupportedException(); public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException(); protected override void Dispose(bool disposing) { _currentChunkStream?.Dispose(); base.Dispose(disposing); } }
使用时替换原有流即可,其余解密逻辑保持不变:
var containerClient =_blobServiceClient.GetBlobContainerClient(sourceContainer); var blobClient = containerClient.GetBlobClient(blobName); using var pgpStream = new SeekableBlobStream(blobClient); var privateKeyEncoded = Encoding.UTF8.GetString(Convert.FromBase64String(_options.PrivateKey)); Stream outputStream = new MemoryStream(); var privatekeyStreamEncoded = GenerateStreamFromString(privateKeyEncoded); var decryptedStream= await pgp.DecryptStreamAsync(pgpStream, outputStream, PgpUtilities.GetDecoderStream(privatekeyStreamEncoded), _options.PassPhrase);
方案2:调整PGP解密逻辑适配只进流
如果不需要校验PGP包的尾部元数据,可以改用BouncyCastle的流式解密接口,全程不会触发Seek操作,直接使用原生OpenReadAsync返回的只进流即可:
var containerClient =_blobServiceClient.GetBlobContainerClient(sourceContainer); using var pgpStream = await containerClient.GetBlobClient(blobName).OpenReadAsync(); using var decodedStream = PgpUtilities.GetDecoderStream(pgpStream); var pgpFactory = new PgpObjectFactory(decodedStream); var encryptedData = (PgpEncryptedDataList)pgpFactory.NextPgpObject(); var keyEncryptedData = encryptedData.GetEncryptedDataObjects().Cast<PgpPublicKeyEncryptedData>().First(); // 私钥读取逻辑保持不变 var privateKeyEncoded = Encoding.UTF8.GetString(Convert.FromBase64String(_options.PrivateKey)); using var privateKeyStream = GenerateStreamFromString(privateKeyEncoded); var privateKey = ReadPrivateKey(privateKeyStream, _options.PassPhrase); // 常规BouncyCastle私钥读取逻辑即可 using var clearStream = keyEncryptedData.GetDataStream(privateKey); var clearFactory = new PgpObjectFactory(clearStream); // 后续读取clearFactory中的解密数据即可
内容的提问来源于stack exchange,提问作者suraj_123
相关产品推荐
相关产品推荐

