透過資料移動程式庫傳輸資料

注意

本文包含使用 Azure 儲存體資料行動程式庫 2.0.XX 版的指導。 2.0.XX 版目前處於維護模式,而且程式庫只會接收資料完整性和安全性問題的修正程式。 不會新增新功能或特性,而且程式庫不支援新的儲存體服務版本。

新式資料移動程式庫的 Beta 版本目前正在開發中。 如需詳細資訊,請參閱 GitHub 上的 適用於 .NET 的 Azure 儲存體 Movement Common 用戶端程式庫

Azure 儲存體資料移動程式庫是跨平台開放原始碼程式庫,專為高效能上傳、下載及複製 Blob 和檔案而設計。 資料移動程式庫提供了無法在適用於 .NET 的 Azure 儲存體用戶端程式庫中使用的便捷方法。 這些方法包括設定平行作業數目、追蹤傳輸進度、輕鬆繼續已取消的傳輸等等。

此程式庫也會使用 .NET Core,這表示您在建置適用於 Windows、Linux 和 macOS 的 .NET 應用程式時可以使用它。 若要深入了解 .NET Core,請參閱 .NET Core 文件 (英文)。 這個程式庫也適用於 Windows 的傳統 .NET 架構應用程式。

本文件將示範如何建立可在 Windows、Linux 和 macOS 上執行的 .NET Core 主控台應用程式,並執行下列案例:

  • 將檔案和目錄上傳至 Blob 儲存體。
  • 定義傳輸資料時的平行作業數目。
  • 追蹤資料傳輸進度。
  • 繼續已取消的資料傳輸。
  • 將檔案從 URL 複製到 Blob 儲存體。
  • 從 Blob 儲存體複製到 Blob 儲存體。

必要條件

設定

  1. 如需安裝 .NET Core SDK,請參閱 .NET Core 安裝指南。 選取環境時,請選擇命令列選項。
  2. 從命令列為專案建立目錄。 瀏覽到此目錄中,然後輸入 dotnet new console -o <sample-project-name> 以建立 C# 主控台專案。
  3. 在 Visual Studio Code 中開啟此目錄。 您可以透過在 Windows 中於命令列輸入 code . 來快速完成此步驟。
  4. 從 Visual Studio Code Marketplace 安裝 C# 擴充功能 (英文)。 重新啟動 Visual Studio Code。
  5. 這時,您應該會看到兩個提示。 其中一個是新增「建置和偵錯的必要資產。」按一下 [是]。另一個提示是還原無法解析的相依性。 按一下 [還原]。
  6. 修改 .vscode 下的 launch.json 以將外部終端機作為主控台。 這項設定應讀取為 "console": "externalTerminal"
  7. Visual Studio Code 可讓您偵錯 .NET Core 應用程式。 按 F5 以執行應用程式,並確定您的設定運作正常。 您應該會看到 "Hello World!" 列印至主控台。

將資料移動程式庫新增至專案

  1. 將最新版本的資料移動程式庫新增至 <project-name>.csproj 檔案的 dependencies 區段。 撰寫本文時,此版本為 "Microsoft.Azure.Storage.DataMovement": "0.6.2"
  2. 您應該會看到提示顯示以還原專案。 按一下 [還原] 按鈕。 您也可以在專案目錄的根目錄中輸入命令 dotnet restore,來從命令列還原專案。

修改 <project-name>.csproj

<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>netcoreapp2.0</TargetFramework>
    </PropertyGroup>
    <ItemGroup>
        <PackageReference Include="Microsoft.Azure.Storage.DataMovement" Version="0.6.2" />
        </ItemGroup>
    </Project>

設定應用程式的基本架構

我們要做的第一件事是設定應用程式的「基本架構」程式碼。 此程式碼會提示我們輸入儲存體帳戶的名稱及帳戶金鑰,並使用該認證來建立 CloudStorageAccount 物件。 這個物件是用來在所有的傳輸案例中與儲存體帳戶互動。 該程式碼也會提示我們選擇要執行的傳輸作業類型。

修改 Program.cs

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;
using Microsoft.Azure.Storage;
using Microsoft.Azure.Storage.Blob;
using Microsoft.Azure.Storage.DataMovement;

namespace DMLibSample
{
    public class Program
    {
        public static void Main()
        {
            Console.WriteLine("Enter Storage account name:");
            string accountName = Console.ReadLine();

            Console.WriteLine("\nEnter Storage account key:");
            string accountKey = Console.ReadLine();

            string storageConnectionString = "DefaultEndpointsProtocol=https;AccountName=" + accountName + ";AccountKey=" + accountKey;
            CloudStorageAccount account = CloudStorageAccount.Parse(storageConnectionString);

            ExecuteChoice(account);
        }

        public static void ExecuteChoice(CloudStorageAccount account)
        {
            Console.WriteLine("\nWhat type of transfer would you like to execute?\n1. Local file --> Azure Blob\n2. Local directory --> Azure Blob directory\n3. URL (e.g. Amazon S3 file) --> Azure Blob\n4. Azure Blob --> Azure Blob");
            int choice = int.Parse(Console.ReadLine());

            if(choice == 1)
            {
                TransferLocalFileToAzureBlob(account).Wait();
            }
            else if(choice == 2)
            {
                TransferLocalDirectoryToAzureBlobDirectory(account).Wait();
            }
            else if(choice == 3)
            {
                TransferUrlToAzureBlob(account).Wait();
            }
            else if(choice == 4)
            {
                TransferAzureBlobToAzureBlob(account).Wait();
            }
        }

        public static async Task TransferLocalFileToAzureBlob(CloudStorageAccount account)
        {

        }

        public static async Task TransferLocalDirectoryToAzureBlobDirectory(CloudStorageAccount account)
        {

        }

        public static async Task TransferUrlToAzureBlob(CloudStorageAccount account)
        {

        }

        public static async Task TransferAzureBlobToAzureBlob(CloudStorageAccount account)
        {

        }
    }
}

將本機檔案上傳至 Blob

將方法 GetSourcePathGetBlob 加入到 Program.cs

public static string GetSourcePath()
{
    Console.WriteLine("\nProvide path for source:");
    string sourcePath = Console.ReadLine();

    return sourcePath;
}

public static CloudBlockBlob GetBlob(CloudStorageAccount account)
{
    CloudBlobClient blobClient = account.CreateCloudBlobClient();

    Console.WriteLine("\nProvide name of Blob container:");
    string containerName = Console.ReadLine();
    CloudBlobContainer container = blobClient.GetContainerReference(containerName);
    container.CreateIfNotExistsAsync().Wait();

    Console.WriteLine("\nProvide name of new Blob:");
    string blobName = Console.ReadLine();
    CloudBlockBlob blob = container.GetBlockBlobReference(blobName);

    return blob;
}

修改 TransferLocalFileToAzureBlob 方法:

public static async Task TransferLocalFileToAzureBlob(CloudStorageAccount account)
{
    string localFilePath = GetSourcePath();
    CloudBlockBlob blob = GetBlob(account);
    Console.WriteLine("\nTransfer started...");
    await TransferManager.UploadAsync(localFilePath, blob);
    Console.WriteLine("\nTransfer operation complete.");
    ExecuteChoice(account);
}

此程式碼會提示我們輸入本機檔案的路徑、新的或現有容器的名稱,和新的 Blob 的名稱。 TransferManager.UploadAsync 方法會利用此資訊執行上傳。

F5 以執行應用程式。 您可以使用 Microsoft Azure 儲存體總管檢視儲存體帳戶,確認是否有上傳。

設定平行作業的數目

資料移動程式庫提供的一項功能是設定平行作業的數目,可增加資料傳輸的輸送量。 根據預設,資料移動程式庫將平行作業的數目設為機器核心數的 8 倍。

請留意,在低頻寬環境執行大量的平行作業可能會拖垮網路連線,並造成作業無法完成。 您必須針對此設定進行實驗,以根據您的可用網路頻寬決定最佳的運作方式。

讓我們加入一些程式碼,以便設定平行作業數目。 另外,也加入可用來計算傳輸完成所需時間的程式碼。

SetNumberOfParallelOperations 方法加入到 Program.cs

public static void SetNumberOfParallelOperations()
{
    Console.WriteLine("\nHow many parallel operations would you like to use?");
    string parallelOperations = Console.ReadLine();
    TransferManager.Configurations.ParallelOperations = int.Parse(parallelOperations);
}

修改 ExecuteChoice 方法以使用 SetNumberOfParallelOperations

public static void ExecuteChoice(CloudStorageAccount account)
{
    Console.WriteLine("\nWhat type of transfer would you like to execute?\n1. Local file --> Azure Blob\n2. Local directory --> Azure Blob directory\n3. URL (e.g. Amazon S3 file) --> Azure Blob\n4. Azure Blob --> Azure Blob");
    int choice = int.Parse(Console.ReadLine());

    SetNumberOfParallelOperations();

    if(choice == 1)
    {
        TransferLocalFileToAzureBlob(account).Wait();
    }
    else if(choice == 2)
    {
        TransferLocalDirectoryToAzureBlobDirectory(account).Wait();
    }
    else if(choice == 3)
    {
        TransferUrlToAzureBlob(account).Wait();
    }
    else if(choice == 4)
    {
        TransferAzureBlobToAzureBlob(account).Wait();
    }
}

修改 TransferLocalFileToAzureBlob 方法以使用計時器:

public static async Task TransferLocalFileToAzureBlob(CloudStorageAccount account)
{
    string localFilePath = GetSourcePath();
    CloudBlockBlob blob = GetBlob(account);
    Console.WriteLine("\nTransfer started...");
    Stopwatch stopWatch = Stopwatch.StartNew();
    await TransferManager.UploadAsync(localFilePath, blob);
    stopWatch.Stop();
    Console.WriteLine("\nTransfer operation completed in " + stopWatch.Elapsed.TotalSeconds + " seconds.");
    ExecuteChoice(account);
}

追蹤傳輸進度

知道資料完成傳輸所需的時間很有幫助。 不過,若能在傳輸作業進行期間看到傳輸的進度幫助會更大。 為了達成此案例,我們需要建立 TransferContext 物件。 TransferContext 物件有兩種形式:SingleTransferContextDirectoryTransferContext。 前者用於傳輸單一檔案,後者則用來傳輸檔案的目錄。

將方法 GetSingleTransferContextGetDirectoryTransferContext 加入到 Program.cs

public static SingleTransferContext GetSingleTransferContext(TransferCheckpoint checkpoint)
{
    SingleTransferContext context = new SingleTransferContext(checkpoint);

    context.ProgressHandler = new Progress<TransferStatus>((progress) =>
    {
        Console.Write("\rBytes transferred: {0}", progress.BytesTransferred );
    });

    return context;
}

public static DirectoryTransferContext GetDirectoryTransferContext(TransferCheckpoint checkpoint)
{
    DirectoryTransferContext context = new DirectoryTransferContext(checkpoint);

    context.ProgressHandler = new Progress<TransferStatus>((progress) =>
    {
        Console.Write("\rBytes transferred: {0}", progress.BytesTransferred );
    });

    return context;
}

修改 TransferLocalFileToAzureBlob 方法以使用 GetSingleTransferContext

public static async Task TransferLocalFileToAzureBlob(CloudStorageAccount account)
{
    string localFilePath = GetSourcePath();
    CloudBlockBlob blob = GetBlob(account);
    TransferCheckpoint checkpoint = null;
    SingleTransferContext context = GetSingleTransferContext(checkpoint);
    Console.WriteLine("\nTransfer started...\n");
    Stopwatch stopWatch = Stopwatch.StartNew();
    await TransferManager.UploadAsync(localFilePath, blob, null, context);
    stopWatch.Stop();
    Console.WriteLine("\nTransfer operation completed in " + stopWatch.Elapsed.TotalSeconds + " seconds.");
    ExecuteChoice(account);
}

繼續已取消的傳輸

資料移動程式庫提供的另一項方便功能,是能繼續執行已取消的傳輸。 讓我們加入一些程式碼,使我們能夠輸入 c 來暫時取消傳輸,然後在 3 秒之後繼續傳輸。

修改 TransferLocalFileToAzureBlob

public static async Task TransferLocalFileToAzureBlob(CloudStorageAccount account)
{
    string localFilePath = GetSourcePath();
    CloudBlockBlob blob = GetBlob(account);
    TransferCheckpoint checkpoint = null;
    SingleTransferContext context = GetSingleTransferContext(checkpoint);
    CancellationTokenSource cancellationSource = new CancellationTokenSource();
    Console.WriteLine("\nTransfer started...\nPress 'c' to temporarily cancel your transfer...\n");

    Stopwatch stopWatch = Stopwatch.StartNew();
    Task task;
    ConsoleKeyInfo keyinfo;
    try
    {
        task = TransferManager.UploadAsync(localFilePath, blob, null, context, cancellationSource.Token);
        while(!task.IsCompleted)
        {
            if(Console.KeyAvailable)
            {
                keyinfo = Console.ReadKey(true);
                if(keyinfo.Key == ConsoleKey.C)
                {
                    cancellationSource.Cancel();
                }
            }
        }
        await task;
    }
    catch(Exception e)
    {
        Console.WriteLine("\nThe transfer is canceled: {0}", e.Message);
    }

    if(cancellationSource.IsCancellationRequested)
    {
        Console.WriteLine("\nTransfer will resume in 3 seconds...");
        Thread.Sleep(3000);
        checkpoint = context.LastCheckpoint;
        context = GetSingleTransferContext(checkpoint);
        Console.WriteLine("\nResuming transfer...\n");
        await TransferManager.UploadAsync(localFilePath, blob, null, context);
    }

    stopWatch.Stop();
    Console.WriteLine("\nTransfer operation completed in " + stopWatch.Elapsed.TotalSeconds + " seconds.");
    ExecuteChoice(account);
}

目前為止,checkpoint 的值一律是設為 null。 現在,如果我們取消傳輸,我們會擷取傳輸的最後一個檢查點,然後在新的傳輸內容中使用這個新的檢查點。

將本機目錄傳送至 Blob 儲存體

如果資料移動程式庫一次只能傳輸一個檔案,不免令人失望。 所幸,事實並非如此。 資料移動程式庫可用於傳輸檔案目錄及其子目錄的所有內容。 讓我們加入一些程式碼來這麼做。

首先,將方法 GetBlobDirectory 加入 Program.cs

public static CloudBlobDirectory GetBlobDirectory(CloudStorageAccount account)
{
    CloudBlobClient blobClient = account.CreateCloudBlobClient();

    Console.WriteLine("\nProvide name of Blob container. This can be a new or existing Blob container:");
    string containerName = Console.ReadLine();
    CloudBlobContainer container = blobClient.GetContainerReference(containerName);
    container.CreateIfNotExistsAsync().Wait();

    CloudBlobDirectory blobDirectory = container.GetDirectoryReference("");

    return blobDirectory;
}

然後,修改 TransferLocalDirectoryToAzureBlobDirectory

public static async Task TransferLocalDirectoryToAzureBlobDirectory(CloudStorageAccount account)
{
    string localDirectoryPath = GetSourcePath();
    CloudBlobDirectory blobDirectory = GetBlobDirectory(account);
    TransferCheckpoint checkpoint = null;
    DirectoryTransferContext context = GetDirectoryTransferContext(checkpoint);
    CancellationTokenSource cancellationSource = new CancellationTokenSource();
    Console.WriteLine("\nTransfer started...\nPress 'c' to temporarily cancel your transfer...\n");

    Stopwatch stopWatch = Stopwatch.StartNew();
    Task task;
    ConsoleKeyInfo keyinfo;
    UploadDirectoryOptions options = new UploadDirectoryOptions()
    {
        Recursive = true
    };

    try
    {
        task = TransferManager.UploadDirectoryAsync(localDirectoryPath, blobDirectory, options, context, cancellationSource.Token);
        while(!task.IsCompleted)
        {
            if(Console.KeyAvailable)
            {
                keyinfo = Console.ReadKey(true);
                if(keyinfo.Key == ConsoleKey.C)
                {
                    cancellationSource.Cancel();
                }
            }
        }
        await task;
    }
    catch(Exception e)
    {
        Console.WriteLine("\nThe transfer is canceled: {0}", e.Message);
    }

    if(cancellationSource.IsCancellationRequested)
    {
        Console.WriteLine("\nTransfer will resume in 3 seconds...");
        Thread.Sleep(3000);
        checkpoint = context.LastCheckpoint;
        context = GetDirectoryTransferContext(checkpoint);
        Console.WriteLine("\nResuming transfer...\n");
        await TransferManager.UploadDirectoryAsync(localDirectoryPath, blobDirectory, options, context);
    }

    stopWatch.Stop();
    Console.WriteLine("\nTransfer operation completed in " + stopWatch.Elapsed.TotalSeconds + " seconds.");
    ExecuteChoice(account);
}

這個方法與上傳單一檔案的方法有一些差異。 我們現在使用 TransferManager.UploadDirectoryAsync 和稍早建立的 getDirectoryTransferContext 方法。 此外,我們現在會提供 options 值給上傳作業,這可讓我們指出要在上傳中包含的子目錄。

將檔案從 URL 複製到 Blob

現在,讓我們加入程式碼,以從 URL 將檔案複製到 Azure Blob。

修改 TransferUrlToAzureBlob

public static async Task TransferUrlToAzureBlob(CloudStorageAccount account)
{
    Uri uri = new Uri(GetSourcePath());
    CloudBlockBlob blob = GetBlob(account);
    TransferCheckpoint checkpoint = null;
    SingleTransferContext context = GetSingleTransferContext(checkpoint);
    CancellationTokenSource cancellationSource = new CancellationTokenSource();
    Console.WriteLine("\nTransfer started...\nPress 'c' to temporarily cancel your transfer...\n");

    Stopwatch stopWatch = Stopwatch.StartNew();
    Task task;
    ConsoleKeyInfo keyinfo;
    try
    {
        task = TransferManager.CopyAsync(uri, blob, CopyMethod.ServiceSideAsyncCopy, null, context, cancellationSource.Token);
        while(!task.IsCompleted)
        {
            if(Console.KeyAvailable)
            {
                keyinfo = Console.ReadKey(true);
                if(keyinfo.Key == ConsoleKey.C)
                {
                    cancellationSource.Cancel();
                }
            }
        }
        await task;
    }
    catch(Exception e)
    {
        Console.WriteLine("\nThe transfer is canceled: {0}", e.Message);
    }

    if(cancellationSource.IsCancellationRequested)
    {
        Console.WriteLine("\nTransfer will resume in 3 seconds...");
        Thread.Sleep(3000);
        checkpoint = context.LastCheckpoint;
        context = GetSingleTransferContext(checkpoint);
        Console.WriteLine("\nResuming transfer...\n");
        await TransferManager.CopyAsync(uri, blob, CopyMethod.ServiceSideAsyncCopy, null, context, cancellationSource.Token);
    }

    stopWatch.Stop();
    Console.WriteLine("\nTransfer operation completed in " + stopWatch.Elapsed.TotalSeconds + " seconds.");
    ExecuteChoice(account);
}

這項功能的一個重要使用案例,是當您必須從另一個雲端服務 (例如 AWS) 將資料移動到 Azure 時。 只要您有可以存取資源的 URL,您就可以使用 TransferManager.CopyAsync 方法,輕鬆地將資源移動至 Azure Blob。 此方法也會引進 CopyMethod 參數。 下表顯示此參數的可用選項:

成員名稱 Description
SyncCopy 0 從來源下載資料到記憶體,並將資料從記憶體上傳至目的地。 目前僅適用於從某個 Azure 儲存體資源複製到另一個 Azure 儲存體。
ServiceSideAsyncCopy 1 將開始複製要求傳送至 Azure 儲存體,讓它進行複製; 監視複製作業進度,直到複製完成為止。
ServiceSideSyncCopy 2 使用 Put Block From URLAppend Block From URLPut Page From URL 複製每個區塊的內容。

複製 Blob

資料移動程式庫提供的另一項獨特功能,是可在不同 Azure 儲存體之間複製資源。

修改 TransferAzureBlobToAzureBlob

public static async Task TransferAzureBlobToAzureBlob(CloudStorageAccount account)
{
    CloudBlockBlob sourceBlob = GetBlob(account);
    CloudBlockBlob destinationBlob = GetBlob(account);
    TransferCheckpoint checkpoint = null;
    SingleTransferContext context = GetSingleTransferContext(checkpoint);
    CancellationTokenSource cancellationSource = new CancellationTokenSource();
    Console.WriteLine("\nTransfer started...\nPress 'c' to temporarily cancel your transfer...\n");

    Stopwatch stopWatch = Stopwatch.StartNew();
    Task task;
    ConsoleKeyInfo keyinfo;
    try
    {
        task = TransferManager.CopyAsync(sourceBlob, destinationBlob, CopyMethod.SyncCopy, null, context, cancellationSource.Token);
        while(!task.IsCompleted)
        {
            if(Console.KeyAvailable)
            {
                keyinfo = Console.ReadKey(true);
                if(keyinfo.Key == ConsoleKey.C)
                {
                    cancellationSource.Cancel();
                }
            }
        }
        await task;
    }
    catch(Exception e)
    {
        Console.WriteLine("\nThe transfer is canceled: {0}", e.Message);
    }

    if(cancellationSource.IsCancellationRequested)
    {
        Console.WriteLine("\nTransfer will resume in 3 seconds...");
        Thread.Sleep(3000);
        checkpoint = context.LastCheckpoint;
        context = GetSingleTransferContext(checkpoint);
        Console.WriteLine("\nResuming transfer...\n");
        await TransferManager.CopyAsync(sourceBlob, destinationBlob, CopyMethod.SyncCopy, null, context, cancellationSource.Token);
    }

    stopWatch.Stop();
    Console.WriteLine("\nTransfer operation completed in " + stopWatch.Elapsed.TotalSeconds + " seconds.");
    ExecuteChoice(account);
}

在此範例中,我們將 TransferManager.CopyAsync 中的布林值參數設為 CopyMethod.SyncCopy,指出我們要執行同步複製。 這表示資源會先下載到本機電腦,然後再上傳至 Azure Blob。 同步複製選項是確保複製作業有一致速度的絕佳方式。 相較之下,非同步伺服器端複製取決於伺服器的可用網路頻寬,速度可能會有所變動。 不過,相較於非同步複製,同步複製可能會產生額外的輸出成本。 建議的方法是在與您來源儲存體帳戶位於同一區域的 Azure VM 中使用同步複製,以避免產生輸出成本。

資料移動應用程式現在已完成。 您可在 GitHub 上取得完整的程式碼範例

下一步

Azure 儲存體資料移動程式庫參考文件

提示

使用 Azure 儲存體總管來管理 Azure Blob 儲存體資源。 Azure 儲存體總管是一個 Microsoft 提供的免費獨立應用程式,可讓您管理 Azure Blob 儲存體資源。 使用 Azure 儲存體總管,您能夠以視覺化方式建立、讀取、更新和刪除 Blob 容器和 Blob,以及管理對 Blob 容器和 Blob 的存取權。