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

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

【问题讨论】:

    标签: c# azure azure-storage


    【解决方案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);
                }
            });
        

        【讨论】:

        【解决方案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);                
            }
            

            【讨论】:

              猜你喜欢
              • 2021-06-27
              • 1970-01-01
              • 2015-10-02
              • 2022-01-11
              • 2013-12-08
              • 2020-09-24
              • 2020-03-29
              • 2020-05-20
              • 2021-07-04
              相关资源
              最近更新 更多