使用C# Azure存储库通过ETag删除Blob历史版本失败,求解决方案
问题描述
使用Azure Functions、Azure Blob存储和React前端构建文件管理工具,已实现Blob及其历史版本详情展示,可获取当前版本与历史版本的VersionId和ETag。因大量大文件历史版本导致存储成本过高,需添加删除Blob历史版本功能,当前存储账户已启用软删除和版本管理。
现有代码可通过ETag匹配成功删除当前版本,但使用有效历史版本ETag时删除失败,返回412 ConditionNotMet错误。
现有代码
// Get a reference to a container where the blob is located BlobContainerClient containerClient = _blobService.GetBlobContainerClient(deleteMessage!.ContainerName); Dictionary<string, bool> deletedItemStatus = []; // Get a reference to the blob you want to delete BlobClient blobClient = containerClient.GetBlobClient(deleteMessage.Path); foreach(var eTag in deleteMessage.ETags) { // TODO - this works perfectly well if we delete the current version using its ETag - but we cant delete previous versions... deletedItemStatus.Add(eTag, await blobClient.DeleteIfExistsAsync(DeleteSnapshotsOption.IncludeSnapshots, new BlobRequestConditions { IfMatch = new ETag(eTag) }, cancellationToken: token)); } var deletedOK = deletedItemStatus.All(r => r.Value == true); _logger.LogInformation("File Delete Operation completed successfully? : {Status}", deletedOK); return deletedOK;
错误日志
[2024-02-19T15:35:44.696Z] Status: 412 (The condition specified using HTTP conditional header(s) is not met.) [2024-02-19T15:35:44.699Z] [2024-02-19T15:35:44.701Z] ErrorCode: ConditionNotMet [2024-02-19T15:35:44.702Z] Headers: [2024-02-19T15:35:44.703Z] [2024-02-19T15:35:44.704Z] Server: Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0
确认历史版本ETag有效(刚获取且可用于删除当前版本),但问题仍存在,需解决思路。
解决思路与代码修改
问题根源
BlobClient.DeleteIfExistsAsync默认操作的是Blob的当前版本,即使指定历史版本的ETag,该API也无法定位到历史版本进行删除。历史版本是独立的资源,需要使用BlobVersionClient来单独操作。另外,DeleteSnapshotsOption.IncludeSnapshots是针对快照功能的,和版本管理无关,此处无需使用。
解决方案步骤
- 针对每个要删除的历史版本,通过VersionId或ETag定位到具体版本
- 创建对应版本的
BlobVersionClient实例 - 使用
BlobVersionClient.DeleteIfExistsAsync执行删除操作,可保留ETag匹配条件确保操作的原子性
修改后的代码示例
BlobContainerClient containerClient = _blobService.GetBlobContainerClient(deleteMessage!.ContainerName); Dictionary<string, bool> deletedItemStatus = []; // 获取该Blob的所有版本列表(用于匹配ETag对应的VersionId) var blobVersions = containerClient.GetBlobVersionsAsync(deleteMessage.Path); foreach(var eTag in deleteMessage.ETags) { // 查找对应ETag的版本 var targetVersion = await blobVersions.FirstOrDefaultAsync(v => v.Properties.ETag.ToString() == eTag); if(targetVersion != null) { // 创建对应版本的BlobVersionClient BlobVersionClient versionClient = containerClient.GetBlobVersionClient(deleteMessage.Path, targetVersion.VersionId); // 执行删除操作,保留IfMatch条件验证ETag bool deleted = await versionClient.DeleteIfExistsAsync( conditions: new BlobRequestConditions { IfMatch = new ETag(eTag) }, cancellationToken: token); deletedItemStatus.Add(eTag, deleted); } else { // 未找到对应ETag的版本,标记为删除失败 deletedItemStatus.Add(eTag, false); } } var deletedOK = deletedItemStatus.All(r => r.Value == true); _logger.LogInformation("File Delete Operation completed successfully? : {Status}", deletedOK); return deletedOK;
额外注意事项
- 版本与快照的区别:启用版本管理后生成的历史版本,和快照是不同的资源,不要混淆两者的操作API
- SDK版本:确保使用最新版的
Azure.Storage.BlobsNuGet包,旧版本可能对BlobVersionClient的支持不完善 - 权限验证:确保Azure Functions使用的身份(如托管标识)拥有
Storage Blob Data Contributor或足够的权限删除Blob版本
内容的提问来源于stack exchange,提问作者Ian Robertson
相关产品推荐
相关产品推荐

