簡體   English   中英

Azure 存儲 Blob 重命名

[英]Azure Storage Blob Rename

是否可以使用來自 Web 角色的 Azure 存儲 API 重命名 Azure 存儲 Blob? 我目前唯一的解決方案是將 blob 復制到具有正確名稱的新 blob 並刪除舊 blob。

更新:

我在@IsaacAbrahams 評論和@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();            
    }

免責聲明:這是一種使重命名以同步方式執行的快速而骯臟的方法。 它符合我的目的,但是正如其他用戶指出的那樣,復制可能需要很長時間(最多幾天),所以最好的方法不是像這個答案那樣在 1 種方法中執行此操作,而是:

  • 開始復制過程
  • 輪詢復制操作的狀態
  • 復制完成后刪除原始 blob。

雖然 Azure Blob 服務 API不直接支持重命名或移動 Blob 的功能,但有一種實用的方法可以做到這一點

但是,您可以復制然后刪除。

我最初使用來自@Zidad 的代碼,在低負載情況下它通常可以工作(我幾乎總是重命名小文件,~10kb)。

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

在高負載情況下,我丟失了大約 20% 正在重命名的文件(數千個文件) 正如對他的回答的評論中提到的, 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();
    }
}

雖然這是一篇舊文章,但這篇優秀的博客文章可能會向其他人展示如何快速重命名已上傳的 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();

復制 Blob,然后將其刪除。

測試了 1G 大小的文件,它工作正常。

有關更多信息,請參閱 MSDN 上的示例

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();  
    } 
} 

使用 Monza Cloud 的 Azure Explorer,我可以在一秒鍾內重命名一個 18 GB 的 blob。 Microsoft 的 Azure 存儲資源管理器需要 29 秒來克隆同一個 blob,因此 Monza 沒有進行復制。 我知道它很快,因為在 Monza 重命名后,單擊 Microsoft Azure 存儲資源管理器中的容器立即顯示具有新名稱的 blob。

唯一的方法是將 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;
    }

您現在可以使用ADLS Gen 2 ( Azure Data Lake Storage Gen 2 ) 公共預覽版中的新版本

分層命名空間功能允許您對目錄和文件執行原子操作,其中包括重命名操作。

但是,請注意以下幾點:“在預覽版中,如果啟用分層命名空間,則 Blob 和 Data Lake Storage Gen2 REST API 之間沒有數據或操作的互操作性。此功能將在預覽期間添加。”

您需要確保使用 ADLS Gen 2 創建 blob(文件)以重命名它們。 否則,請等待在預覽時間段內添加 Blob API 和 ADLS Gen 2 之間的互操作性。

還有一種方法無需復制您的 blob 來重命名它,也無需運行任何腳本:將 Azure Blob 存儲安裝到您的操作系統: https : //docs.microsoft.com/bs-latn-ba/azure/storage/blobs/storage -how-to-mount-container-linux

然后你可以使用mv並且你的 blob 將立即被重命名。

使用 Azure 存儲資源管理器是手動重命名 blob 的最簡單方法。 您可以在此處下載https://azure.microsoft.com/en-us/features/storage-explorer/#overview

重命名是不可能的。 這是使用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();

這在 10 萬用戶的實時環境中對我有用,文件大小不超過 100 mb。 這是@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();
 }

如果您將 ContentDisposition 屬性設置為

attachment;filename="yourfile.txt"

通過 http 下載的名稱將是您想要的任何名稱。

我認為 Storage 的構建假設數據將以一種主要用作文件名的唯一標識符的方式存儲。 不過,為所有下載發布共享訪問簽名有點奇怪,所以這對某些人來說並不理想。

但我認為抽象出面向用戶的文件名總體上是一個很好的做法,並鼓勵總體上更穩定的架構。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM