You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 01:54:02