是否可以使用来自 Web 角色的 Azure 存储 API 重命名 Azure 存储 Blob?我目前唯一的解决方案是将 blob 复制到具有正确名称的新 blob 并删除旧 blob。
14 回答
更新:
我在@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();
}
免责声明:这是使重命名以同步方式执行的快速而肮脏的方法。它符合我的目的,但是正如其他用户指出的那样,复制可能需要很长时间(最多几天),所以最好的方法不是像这个答案那样用一种方法来执行这个,而是:
- 开始复制过程
- 轮询复制操作的状态
- 复制完成后删除原始 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/blob/storage -如何安装容器-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();
如果您将 ContentDisposition 属性设置为
attachment;filename="yourfile.txt"
通过 http 下载的名称将是您想要的任何名称。
我认为存储是建立在假设数据将以一种主要用作文件名的唯一标识符的方式存储的。不过,为所有下载发布共享访问签名有点奇怪,所以这对某些人来说并不理想。
但我认为抽象出面向用户的文件名总体上是一种很好的做法,并鼓励整体上更稳定的架构。
这在文件大小不超过 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();
}