是否可以使用Azure Storage API从Web角色重命名Azure存储Blob?目前我唯一的解决方法是将Blob复制到具有正确名称的新Blob中,然后删除旧Blob。
是否可以使用Azure Storage API从Web角色重命名Azure存储Blob?目前我唯一的解决方法是将Blob复制到具有正确名称的新Blob中,然后删除旧Blob。
更新:
在 @IsaacAbrahams 和 @Viggity 的评论以及答案后,我更新了代码。这个版本应该可以避免你不得不将所有内容加载到 MemoryStream 中,并在复制完成之前等待,然后再删除源 Blob。
对于那些使用 Azure 存储 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 Storage 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个方法中执行此操作,而是:
StartCopyFromBlob
可能需要7天才能完成。据您所知,这是否属实? - crthompson有实用的方法可以做到这一点,虽然 Azure Blob Service 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();
}
}
byte
缓冲区,直接将oldBlob
流传输到newBlob
流中。这完全违背了Stream
的目的,而且完全不可扩展。我曾看到这段代码被复制到生产环境中并引发了OOM问题。非常糟糕。 - makhdumi你可以复制,然后再删除。
虽然这篇文章有些年头了,但也许这篇博客文章能够向其他人展示如何快速重命名已上传的 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 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();
复制 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();
}
}
现在,您可以使用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移动到新的目标位置/名称。以下是我的代码:
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;
}