使用 .NET 基于异步计划复制 Blob

本文介绍了如何使用适用于 .NET 的 Azure 存储客户端库基于异步计划复制 Blob。 你可以从同一存储帐户中的源、不同存储帐户中的源或从通过 HTTP GET 请求在给定 URL 上检索的任何可访问对象复制 Blob。 还可以中止挂起的复制操作。

本文中介绍的客户端库方法将使用 Copy Blob REST API 操作,并且可以在想要基于异步计划执行复制时使用。 对于要将数据移动到存储帐户且具有源对象的 URL 的大多数复制方案,请参阅使用 .NET 从源对象 URL 复制 Blob

先决条件

  • 本文假设已经设置了项目来使用适用于 .NET 的 Azure Blob 存储客户端库。 要了解有关设置项目的信息,包括包安装、添加 using 指令和创建授权客户端对象,请参阅开始使用 Azure Blob 存储和 .NET
  • 授权机制必须具有执行复制操作或中止挂起的复制的权限。 若要了解详细信息,请参阅有关以下 REST API 操作的授权指导:

关于基于异步计划复制 Blob

Copy Blob 操作可以异步完成,并且是在尽最大努力的基础上执行的,这意味着该操作不一定会立即开始或在指定的时间范围内完成。 复制操作是在后台安排的,并在服务器具有可用资源时执行。 如果复制发生在同一存储帐户内,则操作可以同步完成。

Copy Blob 操作可以执行以下任何操作:

  • 将源 Blob 复制到具有不同名称的目标 Blob。 目标 Blob 可以是相同类型(块、追加或页类型)的现有 Blob,也可以是复制操作创建的新 Blob。
  • 将源 Blob 复制到具有相同名称的目标 Blob,从而替换目标 Blob。 此类复制操作会移除所有未提交的块,并覆盖目标 Blob 的元数据。
  • 将 Azure 文件服务中的源文件复制到目标 Blob。 目标 Blob 可以是现有的块 Blob,也可以是复制操作创建的新块 Blob。 不支持从文件复制到页 Blob 或追加 Blob。
  • 将快照复制到其基本 Blob 上。 通过将快照提升到基本 Blob 的位置,可还原早期版本的 Blob。
  • 将快照复制到具有不同名称的目标 Blob。 生成的目标 Blob 是可写的 Blob,而不是快照。

若要详细了解 Copy Blob 操作,包括有关属性、索引标记、元数据和计费的信息,请参阅 Copy Blob 注解

基于异步计划复制 Blob

本部分概述了适用于 .NET 的 Azure 存储客户端库提供的基于异步计划执行复制操作的方法。

以下方法将会封装 Copy Blob REST API 操作,并开始从源 Blob 异步复制数据:

StartCopyFromUriStartCopyFromUriAsync 方法返回包含有关复制操作的信息的 CopyFromUriOperation 对象。 如果需要为复制操作提供异步计划,则可以使用这些方法。

在 Azure 中从源复制 blob

如果要在同一存储帐户中复制 Blob,操作可以同步完成。 可以通过 Microsoft Entra ID、共享访问签名 (SAS) 或帐户密钥来授权对源 Blob 的访问。 有关引起改变的同步复制操作,请参阅使用 .NET 从源对象 URL 复制 Blob

如果复制源是其他存储帐户中的 Blob,则操作可以异步完成。 源 Blob 必须是公共的或通过 SAS 令牌获得授权。 SAS 令牌需要包含读取 ('r') 权限。 若要详细了解 SAS 令牌,请参阅使用共享访问签名委派权限

以下示例演示使用异步计划从其他存储帐户复制源 Blob 的方案。 在此示例中,我们将使用追加的用户委派 SAS 令牌创建源 Blob URL。 此示例演示了如何使用客户端库生成 SAS 令牌,但你也可以提供自己的令牌。 此示例还演示了如何在复制操作期间租用源 Blob,以防止有人从其他客户端对该 Blob 进行更改。 Copy Blob 操作会在复制操作开始时保存源 Blob 的 ETag 值。 如果 ETag 值在复制操作完成之前发生了更改,则操作将失败。

//-------------------------------------------------
// Copy a blob from a different storage account
//-------------------------------------------------
public static async Task CopyAcrossStorageAccountsAsync(
    BlobClient sourceBlob,
    BlockBlobClient destinationBlob)
{
    // Lease the source blob to prevent changes during the copy operation
    BlobLeaseClient sourceBlobLease = new(sourceBlob);

    // Create a Uri object with a SAS token appended - specify Read (r) permissions
    Uri sourceBlobSASURI = await GenerateUserDelegationSAS(sourceBlob);

    try
    {
        await sourceBlobLease.AcquireAsync(BlobLeaseClient.InfiniteLeaseDuration);

        // Start the copy operation and wait for it to complete
        CopyFromUriOperation copyOperation = await destinationBlob.StartCopyFromUriAsync(sourceBlobSASURI);
        await copyOperation.WaitForCompletionAsync();
    }
    catch (RequestFailedException ex)
    {
        // Handle the exception
    }
    finally
    {
        // Release the lease once the copy operation completes
        await sourceBlobLease.ReleaseAsync();
    }
}

async static Task<Uri> GenerateUserDelegationSAS(BlobClient sourceBlob)
{
    BlobServiceClient blobServiceClient =
        sourceBlob.GetParentBlobContainerClient().GetParentBlobServiceClient();

    // Get a user delegation key for the Blob service that's valid for 1 day
    UserDelegationKey userDelegationKey =
        await blobServiceClient.GetUserDelegationKeyAsync(DateTimeOffset.UtcNow,
                                                          DateTimeOffset.UtcNow.AddDays(1));

    // Create a SAS token that's also valid for 1 day
    BlobSasBuilder sasBuilder = new BlobSasBuilder()
    {
        BlobContainerName = sourceBlob.BlobContainerName,
        BlobName = sourceBlob.Name,
        Resource = "b",
        StartsOn = DateTimeOffset.UtcNow,
        ExpiresOn = DateTimeOffset.UtcNow.AddDays(1)
    };

    // Specify read permissions for the SAS
    sasBuilder.SetPermissions(BlobSasPermissions.Read);

    // Add the SAS token to the blob URI
    BlobUriBuilder blobUriBuilder = new BlobUriBuilder(sourceBlob.Uri)
    {
        // Specify the user delegation key
        Sas = sasBuilder.ToSasQueryParameters(userDelegationKey,
                                              blobServiceClient.AccountName)
    };

    return blobUriBuilder.ToUri();
}

注意

用户委派 SAS 令牌提供了更高的安全性,因为它们是使用 Microsoft Entra 凭据而不是帐户密钥进行签名的。 若要创建用户委托 SAS 令牌,Microsoft Entra 安全主体需要适当的权限。 有关授权要求,请参阅获取用户委派密钥

从 Azure 外部的源复制 Blob

你可以对可通过 HTTP GET 请求在给定 URL 上检索的任何源对象执行复制操作,包括 Azure 外部的可访问对象。 以下示例演示了从可访问的源对象 URL 复制 Blob 的方案。

//-------------------------------------------------
// Copy a blob from an external source
//-------------------------------------------------
public static async Task CopyFromExternalSourceAsync(
    string sourceLocation,
    BlockBlobClient destinationBlob)
{
    Uri sourceUri = new(sourceLocation);

    // Start the copy operation and wait for it to complete
    CopyFromUriOperation copyOperation = await destinationBlob.StartCopyFromUriAsync(sourceUri);
    await copyOperation.WaitForCompletionAsync();
}

检查复制操作的状态

若要检查 Copy Blob 操作的状态,可以调用 UpdateStatusAsync 并分析响应以获取 x-ms-copy-status 标头的值。

以下代码示例演示了如何检查复制操作的状态:

public static async Task CheckCopyStatusAsync(CopyFromUriOperation copyOperation)
{
    // Check for the latest status of the copy operation
    Response response = await copyOperation.UpdateStatusAsync();

    // Parse the response to find x-ms-copy-status header
    if (response.Headers.TryGetValue("x-ms-copy-status", out string value))
        Console.WriteLine($"Copy status: {value}");
}

中止复制操作

中止挂起的 Copy Blob 操作会导致目标 Blob 长度为零。 但是,目标 Blob 的元数据将包含从源 Blob 复制的新值,或者在复制操作过程中显式设置的新值。 若要在复制之前保留原始元数据,请在调用一个复制方法之前创建目标 Blob 的快照。

若要中止挂起的复制操作,请调用以下操作之一:

这些方法将会封装 Abort Copy Blob REST API 操作,从而取消挂起的 Copy Blob 操作。 以下代码示例演示了如何中止挂起的 Copy Blob 操作:

public static async Task AbortBlobCopyAsync(
    CopyFromUriOperation copyOperation,
    BlobClient destinationBlob)
{
    // Check for the latest status of the copy operation
    Response response = await copyOperation.UpdateStatusAsync();

    // Parse the response to find x-ms-copy-status header
    if (response.Headers.TryGetValue("x-ms-copy-status", out string value))
    {
        if (value == "pending")
        {
            await destinationBlob.AbortCopyFromUriAsync(copyOperation.Id);
            Console.WriteLine($"Copy operation {copyOperation.Id} aborted");
        }
    }
}

资源

若要详细了解如何使用适用于 .NET 的 Azure Blob 存储客户端库来复制 blob,请参阅以下资源。

REST API 操作

Azure SDK for .NET 包含基于 Azure REST API 而生成的库,允许你通过熟悉的 .NET 范例与 REST API 操作进行交互。 本文中介绍的客户端库方法使用以下 REST API 操作:

代码示例

客户端库资源