Azure Blob Storage调用StartCopyFromUriAsync()偶现零字节文件问题
Azure Media Services 复制到Blob Storage 偶尔出现零字节文件的排查与解决
可能的原因
- 源URL过期或临时不可访问:Azure Media Services生成的URL通常有有效期,复制过程中URL失效会导致无法读取源数据,最终生成零字节文件。
- 未检查复制操作状态:仅通过
WaitForCompletionAsync返回的结果判断文件大小,忽略了复制失败的情况。 - 源资产未完全就绪:部分Media Services资产可能还在处理流程中,此时对应的URL无法返回有效数据。
解决方法
1. 复制前验证源URL有效性
在启动复制操作前,先发送HEAD请求确认源文件可访问且非空:
using var httpClient = new HttpClient(); try { var headRequest = new HttpRequestMessage(HttpMethod.Head, url); var headResponse = await httpClient.SendAsync(headRequest); headResponse.EnsureSuccessStatusCode(); if (headResponse.Content.Headers.ContentLength.HasValue && headResponse.Content.Headers.ContentLength.Value == 0) { // 源文件为空,记录日志并跳过 Console.WriteLine($"源文件 {url} 为空,跳过复制"); return; } } catch (HttpRequestException ex) { // 源URL不可访问,处理异常 Console.WriteLine($"验证源URL失败:{ex.Message}"); throw; }
2. 检查复制操作的状态与结果
不要直接依赖返回的文件大小,而是检查复制状态,并重新获取Blob属性确认文件大小:
var blob = container.GetBlobClient(fileName); var operation = await blob.StartCopyFromUriAsync(new Uri(url)); var copyResult = await operation.WaitForCompletionAsync( DelayStrategy.CreateExponentialDelayStrategy( initialDelay: TimeSpan.FromSeconds(1), maxDelay: TimeSpan.FromMinutes(5)), CancellationToken.None); // 检查复制是否成功 if (copyResult.Value.CopyStatus != CopyStatus.Success) { var errorMsg = $"复制失败:状态={copyResult.Value.CopyStatus},描述={copyResult.Value.CopyStatusDescription}"; Console.WriteLine(errorMsg); throw new InvalidOperationException(errorMsg); } // 重新获取Blob属性确认文件大小 var blobProperties = await blob.GetPropertiesAsync(); var fileSize = blobProperties.Value.ContentLength; if (fileSize == 0) { // 删除无效的零字节文件,可加入重试逻辑 await blob.DeleteAsync(); Console.WriteLine($"复制生成零字节文件,已删除:{fileName}"); // 此处可触发重试 }
3. 添加重试机制
针对临时网络问题或源URL短暂不可用,使用重试策略提升成功率(示例使用Polly库):
using Polly; // 定义重试策略:针对请求异常或复制失败,重试3次 var retryPolicy = Policy .Handle<HttpRequestException>() .OrResult<Response<BlobCopyInfo>>(r => r.Value.CopyStatus != CopyStatus.Success) .Or<InvalidOperationException>(ex => ex.Message.Contains("零字节文件")) .WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))); await retryPolicy.ExecuteAsync(async () => { var blob = container.GetBlobClient(fileName); // 先删除可能存在的零字节文件 await blob.DeleteIfExistsAsync(); var operation = await blob.StartCopyFromUriAsync(new Uri(url)); var copyResult = await operation.WaitForCompletionAsync( DelayStrategy.CreateExponentialDelayStrategy( initialDelay: TimeSpan.FromSeconds(1), maxDelay: TimeSpan.FromMinutes(5)), CancellationToken.None); if (copyResult.Value.CopyStatus != CopyStatus.Success) { return copyResult; } var properties = await blob.GetPropertiesAsync(); if (properties.Value.ContentLength == 0) { await blob.DeleteAsync(); throw new InvalidOperationException("复制得到零字节文件"); } return copyResult; });
4. 确认Media Services资产状态
确保待复制的资产已处理完成,处于可用状态:
// 初始化Media Services客户端 var mediaServicesClient = new AzureMediaServicesClient(new DefaultAzureCredential()) { SubscriptionId = "你的订阅ID", ResourceGroupName = "资源组名称", AccountName = "AMS账户名称" }; var asset = await mediaServicesClient.Assets.GetAsync("资源组名称", "AMS账户名称", "资产名称"); if (asset.Value.ProvisioningState != AssetProvisioningState.Succeeded) { Console.WriteLine($"资产 {asset.Value.Name} 未就绪,状态:{asset.Value.ProvisioningState}"); return; }
内容的提问来源于stack exchange,提问作者Adam
相关产品推荐
相关产品推荐

