在 Azure 存储客户端 2.0 中将一个 Azure blob 复制到另一个 blob

Posted

技术标签:

【中文标题】在 Azure 存储客户端 2.0 中将一个 Azure blob 复制到另一个 blob【英文标题】:Copying one Azure blob to another blob in Azure Storage Client 2.0 【发布时间】:2013-01-04 06:17:11 【问题描述】:

在旧的 1.7 存储客户端中有一个 CloudBlob.CopyFromBlob(otherBlob) 方法,但在 2.0 版本中似乎没有。复制 blob 的推荐最佳做法是什么?我确实看到了 ICloudBlob.BeginStartCopyFromBlob 方法。如果这是合适的方法,我该如何使用它?

【问题讨论】:

【参考方案1】:

Gaurav Mantri 写了一系列关于 Azure Storage 2.0 版本的文章。我从他的 Storage Client Library 2.0 – Migrating Blob Storage Code 博客文章中提取了这段代码,用于 Blob Copy

CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
CloudBlobClient cloudBlobClient = storageAccount.CreateCloudBlobClient();
CloudBlobContainer sourceContainer = cloudBlobClient.GetContainerReference(containerName);
CloudBlobContainer targetContainer = cloudBlobClient.GetContainerReference(targetContainerName);
string blobName = "<Blob Name e.g. myblob.txt>";
CloudBlockBlob sourceBlob = sourceContainer.GetBlockBlobReference(blobName);
CloudBlockBlob targetBlob = targetContainer.GetBlockBlobReference(blobName);
targetBlob.StartCopyFromBlob(sourceBlob);

【讨论】:

我正在尝试使用它在同一个存储帐户中制作 blob 的副本,但我收到 409 CONFLICT 错误。消息是“此操作的 blob 类型无效”。我尝试将它同时视为页面 blob 和块 blob(我很确定我是页面 blob) 已解决。原来源是一个页面 blob,但目标已经创建(在以前的失败尝试中)作为块 blob。删除目的地并重试有效。 这是服务器端副本吗?【参考方案2】:

使用 Storage 6.3(比原始问题更新的库)和异步方法使用 StartCopyAsync (MSDN)

  CloudStorageAccount storageAccount = CloudStorageAccount.Parse("Your Connection");

  CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
  CloudBlobContainer container = blobClient.GetContainerReference("YourContainer");

  CloudBlockBlob source = container.GetBlockBlobReference("Your Blob");
  CloudBlockBlob target = container.GetBlockBlobReference("Your New Blob");
        
  await target.StartCopyAsync(source);

【讨论】:

【参考方案3】:

仅供参考,从 SDK 的最新版本 (7.x) 开始,这不再有效,因为 BeginStartCopyBlob 函数不再存在。

你可以这样做:

// this tunnels the data via your program,
// so it reuploads the blob instead of copying it on service side
using (var stream = await sourceBlob.OpenReadAsync())

  await destinationBlob.UploadFromStreamAsync(stream);

正如@(Alexey Shcherbak) 所提到的,这是一种更好的方法:

await targetCloudBlob.StartCopyAsync(sourceCloudBlob.Uri);

while (targetCloudBlob.CopyState.Status == CopyStatus.Pending)

    await Task.Delay(500);
    // Need to fetch or "CopyState" will never update
    await targetCloudBlob.FetchAttributesAsync(); 


if (targetCloudBlob.CopyState.Status != CopyStatus.Success)

    throw new Exception("Copy failed: " + targetCloudBlob.CopyState.Status);

【讨论】:

这将重新上传 blob。正如 Aaron Sherman 提到的 - 对于较新的库,应该使用 StartCopyAsync 如果你可以添加 Vangaorth 的 FetchAttributes() 修正内联,这可能有用 - 在经过相当多的调试之后我才看到。 启动 Windows Azure Storage 8.0 您还需要不断地获取属性 (targetCloudBlob.FetchAttributes())。上面的代码段将因超时而失败(无限循环)。 targetCloudBlob.StartCopyAsync(sourceCloudBlob.Uri) 需要等待。【参考方案4】:

启动 Azure Storage 8,在存储帐户之间移动 Blob 我使用类似于下面的代码,希望对某人有所帮助:

//copy blobs - from
CloudStorageAccount sourceStorageAccount = new CloudStorageAccount(new StorageCredentials(storageFromName, storageFromKey), true);
CloudBlobClient sourceCloudBlobClient = sourceStorageAccount.CreateCloudBlobClient();
CloudBlobContainer sourceContainer = sourceCloudBlobClient.GetContainerReference(containerFromName);

//copy blobs - to
CloudStorageAccount targetStorageAccount = new CloudStorageAccount(new StorageCredentials(storageToName, storageToKey), true);
CloudBlobClient targetCloudBlobClient = targetStorageAccount.CreateCloudBlobClient();
CloudBlobContainer targetContainer = targetCloudBlobClient.GetContainerReference(containerToName);

//create target container if didn't exists
try
    await targetContainer.CreateIfNotExistsAsync();

catch(Exception e)
    log.Error(e.Message);


CloudBlockBlob sourceBlob = sourceContainer.GetBlockBlobReference(blobName);
CloudBlockBlob targetBlob = targetContainer.GetBlockBlobReference(blobName);

try
    //initialize copying
    await targetBlob.StartCopyAsync(sourceBlob.Uri);

catch(Exception ex)
    log.Error(ex.Message);
    //return error, in my case HTTP
    return req.CreateResponse(HttpStatusCode.BadRequest, "Error, source BLOB probably has private access only: " +ex.Message);
 

//fetch current attributes
targetBlob.FetchAttributes();

//waiting for completion
while (targetBlob.CopyState.Status == CopyStatus.Pending)
    log.Info("Status: " + targetBlob.CopyState.Status);
    Thread.Sleep(500);
    targetBlob.FetchAttributes();


//check status
if (targetBlob.CopyState.Status != CopyStatus.Success)
    //return error, in my case HTTP
    return req.CreateResponse(HttpStatusCode.BadRequest, "Copy failed with status: " + targetBlob.CopyState.Status);


//finally remove source in case Copy Status was Success
sourceBlob.Delete();

//and return success (in my case HTTP)
return req.CreateResponse(HttpStatusCode.OK, "Done.");

【讨论】:

【参考方案5】:

Naveen 已经解释了使用 StartCopyFromBlob(同步方法)的正确语法。您提到的方法 (BeginStartCopyFromBlob) 是异步替代方法,您可以将其与 Task 结合使用,例如:

    var blobClient = account.CreateCloudBlobClient();

    // Upload picture.
    var picturesContainer = blobClient.GetContainerReference("pictures");
    picturesContainer.CreateIfNotExists();
    var myPictureBlob = picturesContainer.GetBlockBlobReference("me.png");
    using (var fs = new FileStream(@"C:\Users\Public\Pictures\Sample Pictures\Chrysanthemum.jpg", FileMode.Open))
        myPictureBlob.UploadFromStream(fs);

    // Backup picture.
    var backupContainer = blobClient.GetContainerReference("backup");
    backupContainer.CreateIfNotExists();
    var backupBlob = picturesContainer.GetBlockBlobReference("me.png");

    var task = Task.Factory.FromAsync<string>(backupBlob.BeginStartCopyFromBlob(myPictureBlob, null, null), backupBlob.EndStartCopyFromBlob);
    task.ContinueWith((t) =>
    
        if (!t.IsFaulted)
        
            while (true)
            
                Console.WriteLine("Copy state for 0: 1", backupBlob.Uri, backupBlob.CopyState.Status);
                Thread.Sleep(500);
            
        
        else
        
            Console.WriteLine("Error: " + t.Exception);
        
    );

【讨论】:

StartCopyFromBlob 也是异步操作,因为copy blob operation is asynchronous【参考方案6】:

对我来说,WindowsAzure.Storage 8.0.1,James Hancock 的解决方案做了服务器端复制,但客户端复制状态卡在Pending(永远循环)。解决方案是在Thread.sleep(500) 之后在targetCloudBlob 上调用FetchAttributes()

// Aaron Sherman's code 

targetCloudBlob.StartCopy(sourceCloudBlob.Uri);

while (targetCloudBlob.CopyState.Status == CopyStatus.Pending)

    Thread.Sleep(500);
    targetCloudBlob.FetchAttributes();


// James Hancock's remaining code

Official Microsoft documentation (async example)

【讨论】:

【参考方案7】:

自从之前的帖子发布以来,API 似乎已经被清理了一点。

// _client is a BlobServiceClient injected via DI in the constructor.

BlobContainerClient sourceContainerClient = _client.GetBlobContainerClient(sourceContainerName);
BlobClient sourceClient = sourceContainerClient.GetBlobClient(blobName);

BlobContainerClient destContainerClient = _client.GetBlobContainerClient(destContainerName);
BlobClient destClient = destContainerClient.GetBlobClient(blobName);

// assume that if the following doesn't throw an exception, then it is successful.
CopyFromUriOperation operation = await destClient.StartCopyFromUriAsync(sourceClient.Uri);
await operation.WaitForCompletionAsync();

operation.WaitForCompletionAsync 的文档说:

定期调用服务器,直到长时间运行的操作完成。该方法会周期性调用UpdateStatusAsync,直到HasCompleted为真,然后返回操作的最终结果。

查看此方法的源代码似乎调用了BlobBaseClient.GetProperties(或异步版本),这将在错误时引发RequestFailureException

【讨论】:

【参考方案8】:

这是我的简短回答。

public void Copy(CloudBlockBlob srcBlob, CloudBlobContainer destContainer)

    CloudBlockBlob destBlob;

    if (srcBlob == null)
    
        throw new Exception("Source blob cannot be null.");
    

    if (!destContainer.Exists())
    
        throw new Exception("Destination container does not exist.");
    

    //Copy source blob to destination container
    string name = srcBlob.Uri.Segments.Last();
    destBlob = destContainer.GetBlockBlobReference(name);
    destBlob.StartCopyAsync(srcBlob);                

【讨论】:

以上是关于在 Azure 存储客户端 2.0 中将一个 Azure blob 复制到另一个 blob的主要内容,如果未能解决你的问题,请参考以下文章

Azure 函数中的 Az.Functions 模块引发错误

Azure:使用容器创建存储帐户并在 Python 中将 blob 上传到其中

用于获取 Azure Monitor 日志的 AZ 命令

使用 Azure AZ Powershell Cmdlet 列出 Azure 安全中心策略状态

Azure SQL 连接到选定网络的 Azure 存储

如何更新所有 Azure Powershell Az 模块?