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

使用.NET客户端库在Azure存储跨容器复制文件时遇401认证错误

Azure跨存储账户Blob复制报错401(CannotVerifyCopySource)解决办法

问题场景

跨两个公开状态的Azure存储账户容器复制Blob,本地使用RBAC所有者权限可正常执行上传、下载、列内容操作,但调用StartCopyFromUri时返回401错误,错误码CannotVerifyCopySource。

错误原因

StartCopyFromUri是服务器端异步复制操作,由目标存储账户的服务端直接发起请求访问源Blob,而非客户端读取后上传。即使客户端拥有RBAC权限,目标存储服务的请求无法继承客户端权限;若仅依赖容器公开设置,服务器端请求可能因匿名访问的限制(或服务端默认不使用匿名访问)导致认证失败。

解决方案

方案1:为源Blob生成SAS令牌(推荐,服务器端复制)

给源Blob生成带读取权限的SAS令牌,将其附加到源URI后传入StartCopyFromUri,让目标存储服务能通过SAS认证访问源Blob。

基于RBAC的UserDelegation SAS(无需账户密钥,更安全)

var targetContainer = "targetContainer";  
var targetStorageAccount = "targetStorage";
var targetUrl = $"https://{targetStorageAccount}.blob.core.windows.net";
var targetServiceClient = new BlobServiceClient(new Uri(targetUrl), new DefaultAzureCredential());

var stageStorageAccount = "sourceStorage"; 
var stageContainer = "sourceContainer";
var stageUrl = $"https://{stageStorageAccount}.blob.core.windows.net";
var stageServiceClient = new BlobServiceClient(new Uri(stageUrl), new DefaultAzureCredential());

BlobContainerClient stageContainerClient = stageServiceClient.GetBlobContainerClient(stageContainer);
BlobContainerClient targetContainerClient = targetServiceClient.GetBlobContainerClient(targetContainer);
BlobClient sourceBlobClient = stageContainerClient.GetBlobClient(fileName);
BlobClient targetBlobClient = targetContainerClient.GetBlobClient(fileName);

// 获取用户委托密钥(基于RBAC权限)
var userDelegationKey = await stageServiceClient.GetUserDelegationKeyAsync(DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddHours(1));
// 构建SAS令牌,授予读取权限,有效期1小时
var sasBuilder = new BlobSasBuilder(BlobSasPermissions.Read, DateTimeOffset.UtcNow.AddHours(1))
{
    BlobContainerName = stageContainer,
    BlobName = fileName,
    Resource = "b", // 指定为Blob资源
    UserDelegationKey = userDelegationKey
};
// 生成带SAS的源URI
var sourceUriWithSas = sourceBlobClient.GenerateSasUri(sasBuilder);

// 执行服务器端复制
var result = await targetBlobClient.StartCopyFromUriAsync(sourceUriWithSas);

基于账户密钥的SAS(需存储账户密钥)

如果无法使用UserDelegation SAS,可直接用存储账户密钥生成SAS:

// 构建SAS令牌
var sasBuilder = new BlobSasBuilder(BlobSasPermissions.Read, DateTimeOffset.UtcNow.AddHours(1))
{
    BlobContainerName = stageContainer,
    BlobName = fileName,
    Resource = "b"
};
// 生成带SAS的源URI
var sourceUriWithSas = sourceBlobClient.GenerateSasUri(sasBuilder);

// 执行复制
var result = await targetBlobClient.StartCopyFromUriAsync(sourceUriWithSas);

方案2:客户端复制(下载后上传)

绕过服务器端复制,由客户端先下载源Blob,再上传到目标容器,依赖客户端已有的RBAC权限即可完成操作:

var targetContainer = "targetContainer";  
var targetStorageAccount = "targetStorage";
var targetUrl = $"https://{targetStorageAccount}.blob.core.windows.net";
var targetServiceClient = new BlobServiceClient(new Uri(targetUrl), new DefaultAzureCredential());

var stageStorageAccount = "sourceStorage"; 
var stageContainer = "sourceContainer";
var stageUrl = $"https://{stageStorageAccount}.blob.core.windows.net";
var stageServiceClient = new BlobServiceClient(new Uri(stageUrl), new DefaultAzureCredential());

BlobContainerClient stageContainerClient = stageServiceClient.GetBlobContainerClient(stageContainer);
BlobContainerClient targetContainerClient = targetServiceClient.GetBlobContainerClient(targetContainer);
BlobClient sourceBlobClient = stageContainerClient.GetBlobClient(fileName);
BlobClient targetBlobClient = targetContainerClient.GetBlobClient(fileName);

// 下载源Blob到内存流
using (var stream = new MemoryStream())
{
    await sourceBlobClient.DownloadToAsync(stream);
    stream.Position = 0;
    // 上传到目标Blob(覆盖已存在的同名Blob)
    await targetBlobClient.UploadAsync(stream, overwrite: true);
}

内容的提问来源于stack exchange,提问作者Jashvita

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 19:05:15