Azure Blob Storage列表查询如何实现支持双向跳转的分页功能
Azure Blob Storage 支持跳页/前后翻页的分页实现方案
Azure Blob Storage 原生的列表API仅支持基于 continuation token 的顺序分页,不支持直接按页码偏移查询,因此没有原生的跳页能力,需要通过额外的逻辑封装实现。
适用场景(数千~数万级文件量)
你当前场景只有数千个文件,可通过缓存页码与continuation token的映射关系实现需求,实现成本最低。
实现思路
- 新增内存缓存存储指定容器+指定页大小下的「页码-续传Token」映射关系
- 接口入参从续传Token改为页码、页大小,内部根据页码匹配对应的续传Token查询
- 缓存设置合理的过期时间,抵消Blob列表变动带来的映射失效问题
代码调整
1. 调整Dto定义
public class BlobItemDto { public string Name { get; set; } = default!; public string Url { get; set; } = default!; } public class BlobPageDto { public IEnumerable<BlobItemDto>? BlobItems { get; set; } public string? ContinuationToken { get; set; } // 新增分页相关字段 public int CurrentPage { get; set; } public int PageSize { get; set; } public bool HasNextPage { get; set; } }
2. 改造分页逻辑(依赖IMemoryCache做缓存)
private readonly IMemoryCache _cache; private readonly string _connectionString; // 构造函数注入缓存和配置 public YourBlobService(IConfiguration configuration, IMemoryCache cache) { _connectionString = configuration.GetValue<string>("BlobStorage:ConnectionString"); _cache = cache; } public async Task<BlobPageDto> GetBlobPageAsync(string containerName, int pageNumber, int pageSize) { var blobPage = new BlobPageDto(); // 入参合法性校验 pageNumber = pageNumber < 1 ? 1 : pageNumber; pageSize = pageSize is < 1 or > 5000 ? 10 : pageSize; // Azure Blob单页最大支持5000条 try { var client = new BlobContainerClient(_connectionString, containerName); await client.CreateIfNotExistsAsync(); // 缓存Key:容器名+页大小,保证不同容器不同页大小的映射相互独立 var cacheKey = $"BlobPageTokenMap_{containerName}_{pageSize}"; // 缓存结构:字典,key为页码,value为对应页的续传Token(第1页对应Token为null) var tokenMap = _cache.Get<Dictionary<int, string?>>(cacheKey) ?? new Dictionary<int, string?> { {1, null} }; string? targetToken = null; // 如果要查询的页码已缓存,直接取对应的Token if (tokenMap.ContainsKey(pageNumber)) { targetToken = tokenMap[pageNumber]; } // 如果页码超过已缓存的最大页码,遍历拉取直到拿到目标页的Token else { var currentMaxPage = tokenMap.Keys.Max(); var currentToken = tokenMap[currentMaxPage]; while (currentMaxPage < pageNumber) { var tempPage = await client.GetBlobsAsync().AsPages(currentToken, pageSize).FirstAsync(); currentToken = tempPage.ContinuationToken; currentMaxPage++; tokenMap.TryAdd(currentMaxPage, currentToken); // 已经到最后一页,跳出循环 if (string.IsNullOrEmpty(currentToken)) { pageNumber = currentMaxPage; break; } } targetToken = tokenMap[pageNumber]; // 更新缓存,有效期5分钟,可根据业务调整 _cache.Set(cacheKey, tokenMap, TimeSpan.FromMinutes(5)); } // 查询目标页数据 var resultPage = await client.GetBlobsAsync().AsPages(targetToken, pageSize).FirstAsync(); var blobItems = new List<BlobItemDto>(); foreach (var item in resultPage.Values) { var blobClient = client.GetBlobClient(item.Name); blobItems.Add(new BlobItemDto { Name = item.Name, Url = blobClient.Uri.ToString() }); } blobPage = new BlobPageDto { BlobItems = blobItems, ContinuationToken = resultPage.ContinuationToken, CurrentPage = pageNumber, PageSize = pageSize, HasNextPage = !string.IsNullOrEmpty(resultPage.ContinuationToken) }; } catch (RequestFailedException e) { // 按需处理异常日志 } return blobPage; }
大数据量场景适配
如果后续文件量增长到十万级以上,上述缓存方案首次拉取大页码的开销会明显升高,可采用以下方案优化:
- 定期同步Blob元数据到支持偏移分页的存储(如关系型数据库、Cosmos DB),分页查询直接走该存储,性能更强
- 保留连续翻页逻辑,限制直接跳转大页码的操作,降低无效查询开销
内容的提问来源于stack exchange,提问作者Shameem
相关产品推荐
相关产品推荐

