Azure 存储 Blob 重命名

Posted

技术标签:

【中文标题】Azure 存储 Blob 重命名【英文标题】:Azure Storage Blob Rename 【发布时间】:2010-09-17 10:49:38 【问题描述】:

是否可以使用来自 Web 角色的 Azure 存储 API 重命名 Azure 存储 Blob?我目前唯一的解决方案是将 blob 复制到具有正确名称的新 blob 并删除旧 blob。

【问题讨论】:

现在,是的,使用 ADLS Gen 2 和分层命名空间 - docs.microsoft.com/en-us/azure/storage/data-lake-storage/… 【参考方案1】:

更新:

我在@IsaacAbrahams cmets 和@Viggity 的回答之后更新了代码,这个版本应该可以防止您将所有内容加载到 MemoryStream 中,并等到复制完成后再删除源 blob。


对于迟到但使用 Azure Storage API V2 偶然发现这篇文章的任何人,这里有一个 快速而肮脏的扩展方法(+异步版本):

public static class BlobContainerExtensions 

   public static void Rename(this CloudBlobContainer container, string oldName, string newName)
   
      //Warning: this Wait() is bad practice and can cause deadlock issues when used from ASP.NET applications
      RenameAsync(container, oldName, newName).Wait();
   

   public static async Task RenameAsync(this CloudBlobContainer container, string oldName, string newName)
   
      var source = await container.GetBlobReferenceFromServerAsync(oldName);
      var target = container.GetBlockBlobReference(newName);

      await target.StartCopyFromBlobAsync(source.Uri);

      while (target.CopyState.Status == CopyStatus.Pending)
            await Task.Delay(100);

      if (target.CopyState.Status != CopyStatus.Success)
          throw new Exception("Rename failed: " + target.CopyState.Status);

      await source.DeleteAsync();
    

Azure 存储 7.0 更新

    public static async Task RenameAsync(this CloudBlobContainer container, string oldName, string newName)
    
        CloudBlockBlob source =(CloudBlockBlob)await container.GetBlobReferenceFromServerAsync(oldName);
        CloudBlockBlob target = container.GetBlockBlobReference(newName);


        await target.StartCopyAsync(source);

        while (target.CopyState.Status == CopyStatus.Pending)
            await Task.Delay(100);

        if (target.CopyState.Status != CopyStatus.Success)
            throw new Exception("Rename failed: " + target.CopyState.Status);

        await source.DeleteAsync();            
    

免责声明:这是一种使重命名以同步方式执行的快速而肮脏的方法。它符合我的目的,但是正如其他用户所指出的那样,复制可能需要很长时间(最多几天),所以最好的方法不是像这个答案那样用一种方法来执行这个,而是:

开始复制过程 轮询复制操作的状态 复制完成后删除原始 blob。

【讨论】:

@BrianMacKay 提到 StartCopyFromBlob 可能需要 7 天才能完成。据你所知,这有什么真相吗? 嗨@Paqogomez,也许根据某处的SLA,但根据我的经验,它很快(在毫秒到秒的范围内) AFAIK StartCopyFromBlob 将在复制操作开始后返回。复制完成后不会返回!要确定复制操作何时完成,您需要轮询 blob 的最新属性并查看复制操作何时完成。 在高负载情况下,我丢失了大约 20% 的我正在重命名的文件,因为删除击败了副本。操作不要排队。在此处修复:***.com/questions/3734672/azure-storage-blob-rename/… @Vikram 如果我快速看一下,您没有在异步操作上使用“等待”。我认为您应该首先研究 C# 中 async/await 结构的基础知识,然后提出一个新问题(如果您希望我回答,请点击此处链接),这应该会给您一个更好的答案。【参考方案2】:

有 practical way to do so,虽然 Azure Blob Service API 不直接支持重命名或移动 blob 的能力。

【讨论】:

尝试 ADLS Gen 2 API,您可以在其中重命名 blob,就像在原子操作中重命名文件一样。 - azure.microsoft.com/en-us/services/storage/data-lake-storage @SaherAhwal 文件资源不是 blob 资源。到目前为止,还没有 API 可以在原子操作中重命名 blob @lerthe61 是的,这就是您需要使用 ADLS Gen 2 并启用分层命名空间的原因。 您现在可以执行此操作,请参阅***.com/revisions/38973244/4【参考方案3】:

但是,您可以复制然后删除。

【讨论】:

如果您复制,请确保复制实际数据和元数据,然后删除。不要使用 StartCopyFromBlob 然后删除。我丢失了 20% 的重命名文件,因为在删除生效之前复制没有完成。 ***.com/questions/3734672/azure-storage-blob-rename/…【参考方案4】:

我最初使用来自@Zidad 的代码,在低负载情况下它通常可以工作(我几乎总是重命名小文件,~10kb)。

不要StartCopyFromBlob 然后Delete!!!!!!!!!!!!!!!

在高负载情况下,我丢失了大约 20% 的重命名文件(数千个文件)。正如他的回答中的 cmets 所述,StartCopyFromBlob 只是开始复制。 您无法等待复制完成。

保证副本完成的唯一方法是下载并重新上传。这是我更新的代码:

public void Rename(string containerName, string oldFilename, string newFilename)

    var oldBlob = GetBlobReference(containerName, oldFilename);
    var newBlob = GetBlobReference(containerName, newFilename);

    using (var stream = new MemoryStream())
    
        oldBlob.DownloadToStream(stream);
        stream.Seek(0, SeekOrigin.Begin);
        newBlob.UploadFromStream(stream);

        //copy metadata here if you need it too

        oldBlob.Delete();
    

【讨论】:

嗨 Viggity,谢谢,@IsaacAbraham 似乎是对的,对此感到抱歉。我已经用警告更新了我的答案。 显然您还可以检查一个状态,它允许您重命名 blob,而无需像您一样将其全部下载到内存中,我会更新我的答案... @zidad,有趣的方法。在我的特殊情况下,我不想让它异步,因为它会处理其他一些事情。感谢您的更新。 为什么人们会这样滥用 MemoryStreams?这太糟糕了。您可以使用一个小的byte 缓冲区将oldBlob 流直接传送到newBlob 流中。这完全超出了Stream 的目的,并且完全不可扩展。我已经看到将相同的代码复制到生产环境中,这导致了 OOM 问题。太可怕了。 我所有的文件都很小,没关系。对不起【参考方案5】:

虽然这是一篇旧帖子,但也许这个 excellent blog post 会向其他人展示如何快速重命名已上传的 blob。

以下是重点:

//set the azure container
string blobContainer = "myContainer";
//azure connection string
string dataCenterSettingKey = string.Format("DefaultEndpointsProtocol=https;AccountName=0;AccountKey=1", "xxxx",
                                            "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
//setup the container object
CloudStorageAccount cloudStorageAccount = CloudStorageAccount.Parse(dataCenterSettingKey);
CloudBlobClient blobClient = cloudStorageAccount.CreateCloudBlobClient();
CloudBlobContainer container = blobClient.GetContainerReference(blobContainer);

// Set permissions on the container.
BlobContainerPermissions permissions = new BlobContainerPermissions();
permissions.PublicAccess = BlobContainerPublicAccessType.Blob;
container.SetPermissions(permissions);

//grab the blob
CloudBlob existBlob = container.GetBlobReference("myBlobName");
CloudBlob newBlob = container.GetBlobReference("myNewBlobName");
//create a new blob
newBlob.CopyFromBlob(existBlob);
//delete the old
existBlob.Delete();

【讨论】:

考虑用相关要点的简要总结来更新您的答案。那么即使链接断开,它仍然对其他人有用。 据我所知,这在 Azure Storage API 2 中已经过时了...现在 CopyFromBlob 已成为 StartCopyFromBlob,我听说这会将您的 blob 放入一个重命名队列中最多需要 7 天! 这不是我的经验。虽然在 SLA 中可能会这样说,但它的创建和复制速度非常快。 v2 api 将 CopyFromBlob 转换为 StartCopyFromBlob。在高负载情况下,我丢失了 20% 的重命名文件。不好。 ***.com/questions/3734672/azure-storage-blob-rename/… @paqogomez - 实际上性能因其他 Azure 客户而异。我已与 Microsoft 联系,他们已确认有时复制需要几天时间。【参考方案6】:

复制 blob,然后将其删除。

针对 1G 大小的文件进行了测试,运行正常。

有关详细信息,请参阅 MSDN 上的sample。

StorageCredentials cred = new StorageCredentials("[Your?storage?account?name]", "[Your?storage?account?key]");  
CloudBlobContainer container = new CloudBlobContainer(new Uri("http://[Your?storage?account?name].blob.core.windows.net/[Your container name] /"), cred);  

string fileName = "OldFileName";  
string newFileName = "NewFileName";  
await container.CreateIfNotExistsAsync();  

CloudBlockBlob blobCopy = container.GetBlockBlobReference(newFileName);  

if (!await blobCopy.ExistsAsync())  
  
    CloudBlockBlob blob = container.GetBlockBlobReference(fileName);  

    if (await blob.ExistsAsync())  
      
           // copy
           await blobCopy.StartCopyAsync(blob);                               
           // then delete
           await blob.DeleteIfExistsAsync();  
     
 

【讨论】:

【参考方案7】:

使用 Monza Cloud 的 Azure Explorer,我可以在一秒钟内重命名一个 18 GB 的 blob。微软的 Azure 存储资源管理器需要 29 秒来克隆相同的 blob,所以蒙扎不是 做一个副本。我知道这很快,因为在 Monza 重命名之后,单击 Microsoft Azure 存储资源管理器中的容器会立即显示具有新名称的 blob。

【讨论】:

【参考方案8】:

目前唯一的方法是将 src blob 移动到新的目的地/名称。这是我执行此操作的代码

 public async Task<CloudBlockBlob> RenameAsync(CloudBlockBlob srcBlob, CloudBlobContainer destContainer,string name)
    
        CloudBlockBlob destBlob;

        if (srcBlob == null && srcBlob.Exists())
        
            throw new Exception("Source blob cannot be null and should exist.");
        

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

        //Copy source blob to destination container            
        destBlob = destContainer.GetBlockBlobReference(name);
        await destBlob.StartCopyAsync(srcBlob);
        //remove source blob after copy is done.
        srcBlob.Delete();
        return destBlob;
    

如果您希望将 blob 查找作为方法的一部分,这里是一个代码示例:

    public CloudBlockBlob RenameBlob(string oldName, string newName, CloudBlobContainer container)
    
        if (!container.Exists())
        
            throw new Exception("Destination container does not exist.");
        
        //Get blob reference
        CloudBlockBlob sourceBlob = container.GetBlockBlobReference(oldName);

        if (sourceBlob == null && sourceBlob.Exists())
        
            throw new Exception("Source blob cannot be null and should exist.");
        

        // Get blob reference to which the new blob must be copied
        CloudBlockBlob destBlob = container.GetBlockBlobReference(newName);
        destBlob.StartCopyAsync(sourceBlob);

        //Delete source blob
        sourceBlob.Delete();
        return destBlob;
    

【讨论】:

【参考方案9】:

您现在可以在 ADLS Gen 2 (Azure Data Lake Storage Gen 2) 的公共预览版中使用新版本

Hierarchical Namespace 功能允许您对目录和文件执行原子操作,其中包括 重命名 操作。

但是,请注意以下几点: “在预览版中,如果启用分层命名空间,则 Blob 和 Data Lake Storage Gen2 REST API 之间的数据或操作没有互操作性。此功能将在预览期间添加。”

您需要确保使用 ADLS Gen 2 创建 blob(文件)以重命名它们。否则,请等待在预览期间添加 Blob API 和 ADLS Gen 2 之间的互操作性。

【讨论】:

【参考方案10】:

还有一种方法无需复制您的 blob 即可对其进行重命名,并且无需运行任何脚本:将 Azure Blob 存储安装到您的操作系统:https://docs.microsoft.com/bs-latn-ba/azure/storage/blobs/storage-how-to-mount-container-linux

然后您可以使用mv,您的 blob 将立即重命名。

【讨论】:

【参考方案11】:

使用 Azure 存储资源管理器是手动重命名 Blob 的最简单方法。你可以在这里下载它https://azure.microsoft.com/en-us/features/storage-explorer/#overview

【讨论】:

【参考方案12】:

无法重命名。以下是使用 Azure SDK for .NET v12 的解决方法:

BlobClient sourceBlob = container.GetBlobClient(sourceBlobName);
BlobClient destBlob = container.GetBlobClient(destBlobName);
CopyFromUriOperation ops = await destBlob.StartCopyFromUriAsync(sourceBlob.Uri);

long copiedContentLength = 0;
while (ops.HasCompleted == false)

    copiedContentLength = await ops.WaitForCompletionAsync();
    await Task.Delay(100);

await sourceBlob.DeleteAsync();

【讨论】:

【参考方案13】:

如果您使用

设置 ContentDisposition 属性
attachment;filename="yourfile.txt"

通过 http 下载的名称可以是您想要的任何名称。

我认为 Storage 的构建假设数据将以一种主要用作文件名的唯一标识符的方式存储。不过,为所有下载发布共享访问签名有点奇怪,所以这对某些人来说并不理想。

但我认为抽象出面向用户的文件名总体上是一种很好的做法,并鼓励总体上更稳定的架构。

【讨论】:

我对此投了反对票,因为这个答案实际上并没有重命名 blob,这是 OP 要求的。虽然使用 Content-Disposition 是一个巧妙的技巧,但它并不是真正的解决方案。【参考方案14】:

这在文件大小不超过 100 mb 的 100K 用户的实时环境中对我有用。这与@viggity 的答案类似的同步方法。但不同之处在于它在 Azure 端复制所有内容,因此您不必在服务器上保存 Memorystream 以复制/上传到新 Blob。

 var account = new CloudStorageAccount(new Microsoft.WindowsAzure.Storage.Auth.StorageCredentials(StorageAccountName, StorageAccountKey), true);
 CloudBlobClient blobStorage = account.CreateCloudBlobClient();
 CloudBlobContainer container = blobStorage.GetContainerReference("myBlobContainer");

 string fileName = "OldFileName";  
 string newFileName = "NewFileName"; 

 CloudBlockBlob oldBlob = container.GetBlockBlobReference(fileName);
 CloudBlockBlob newBlob = container.GetBlockBlobReference(newFileName);
 using (var stream = new MemoryStream())
 
      newBlob.StartCopyFromBlob(oldBlob);
      do   while (!newBlob.Exists());
      oldBlob.Delete();
 

【讨论】:

您为什么要使用从未被引用的新 MemorySteam? 对不起,我从@viggity 的答案中分叉了该代码并忘记删除该引用。但是您可以了解如何在服务器端完成它。

以上是关于Azure 存储 Blob 重命名的主要内容,如果未能解决你的问题,请参考以下文章

使用 AspNet 从 Azure Blob 存储下载和重命名文件

重命名 Azure 函数

是否可以使用 Azure 门户重命名 Azure 应用服务计划?

Plupload块大小重命名文件为Blob

如何在 Azure DevOps 中重命名任务组

重命名 SQL Azure 数据库?