使用 .NET 针对 Azure 文件进行开发

了解开发使用 Azure 文件存储来存储数据的 .NET 应用程序的基础知识。 本文介绍了如何创建一个简单的控制台应用程序,以便通过 .NET 和 Azure 文件存储执行以下操作:

  • 获取文件内容。
  • 为文件共享设置最大大小或配额。
  • 为文件创建共享访问签名 (SAS)。
  • 将文件复制到同一存储帐户中的另一个文件。
  • 将文件复制到同一存储帐户中的一个 Blob。
  • 创建文件共享的快照。
  • 从共享快照还原文件。
  • 使用 Azure 存储度量值进行故障排除。

若要了解有关 Azure 文件存储的详细信息,请参阅什么是 Azure 文件存储?

提示

查看 Azure 存储代码示例存储库

如需易用且能够下载和运行的端到端 Azure 存储代码示例,请查看我们的 Azure 存储示例列表。

适用于

文件共享类型 SMB NFS
标准文件共享 (GPv2)、LRS/ZRS Yes No
标准文件共享 (GPv2)、GRS/GZRS Yes No
高级文件共享 (FileStorage)、LRS/ZRS Yes No

了解 .NET API

Azure 文件为客户端应用程序提供两个主要方法:服务器消息块 (SMB) 和 REST。 在 .NET 中,System.IOAzure.Storage.Files.Shares API 将抽象化这些方法。

API 何时使用 说明
System.IO 应用程序:
  • 需要使用 SMB 读取/写入文件
  • 是否在可以通过端口 445 访问 Azure 文件帐户的设备上运行
  • 不需要管理文件共享的任何管理设置
一般情况下,通过 SMB 使用 Azure 文件存储实现的文件 I/O 与使用任何网络文件共享或本地存储设备实现的 I/O 相同。 有关 .NET 中的许多功能(包括文件 I/O)的简介,请参阅控制台应用程序教程。
Azure.Storage.Files.Shares 应用程序:
  • 由于防火墙或 ISP 约束,无法使用 SMB 在端口 445 上访问 Azure 文件存储
  • 需要管理功能,例如能够设置文件共享的配额或创建共享访问签名
本文演示如何通过 REST(而不是 SMB)将 Azure.Storage.Files.Shares 用于文件 I/O 以及如何管理文件共享。

创建控制台应用程序,并获取程序集

可以在任意类型的 .NET 应用中使用 Azure 文件存储客户端库。 这些应用包括 Azure 云应用、Web 应用、桌面应用和移动应用。 为简单起见,我们在本指南中创建一个控制台应用程序。

在 Visual Studio 中创建新的 Windows 控制台应用程序。 以下步骤演示了如何在 Visual Studio 2019 中创建控制台应用程序。 在其他版本的 Visual Studio 中,这些步骤是类似的。

  1. 启动 Visual Studio 并选择“创建新项目”。
  2. 在“创建新项目” 中,选择用于 C# 的“控制台应用(.NET Framework)” ,然后选择“下一步” 。
  3. 在“配置新项目”中输入应用的名称,然后选择“创建”。

将本文中的所有代码示例添加到 Program.cs 文件中的 Program 类。

使用 NuGet 安装所需包

在你的项目中引用以下包:

可以使用 NuGet 获取这些包。 执行以下步骤:

  1. 在“解决方案资源管理器”中,右键单击你的项目并选择“管理 NuGet 包” 。

  2. 在“NuGet 包管理器”中选择“浏览”。 接着搜索并选择“Azure.Core”,然后选择“安装”。

    此步骤将安装该包及其依赖项。

  3. 搜索并安装以下包:

    • Azure.Storage.Blobs
    • Azure.Storage.Files.Shares
    • System.Configuration.ConfigurationManager

将存储帐户凭据保存到 App.config 文件

接下来,将凭据保存到项目的 App.config 文件中。 在“解决方案资源管理器”中,双击 App.config 并编辑该文件,使其类似于以下示例。

请将 myaccount 替换为你的存储帐户名,将 mykey 替换为你的存储帐户密钥。

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <appSettings>
    <add key="StorageConnectionString" 
      value="DefaultEndpointsProtocol=https;AccountName=myaccount;AccountKey=mykey;EndpointSuffix=core.chinacloudapi.cn" />
    <add key="StorageAccountName" value="myaccount" />
    <add key="StorageAccountKey" value="mykey" />
  </appSettings>
</configuration>

注意

Azurite 存储模拟器目前不支持 Azure 文件存储。 连接字符串必须针对云中要使用 Azure 文件存储的 Azure 存储帐户。

添加 using 指令

在“解决方案资源管理器”中打开 Program.cs 文件,并在该文件顶部添加以下 using 指令。

using System;
using System.Configuration;
using System.IO;
using System.Threading.Tasks;
using Azure;
using Azure.Storage;
using Azure.Storage.Blobs;
using Azure.Storage.Files.Shares;
using Azure.Storage.Files.Shares.Models;
using Azure.Storage.Sas;

以编程方式访问文件共享

在 Program.cs 文件中添加以下代码,以便以编程方式访问文件共享。

如果文件共享尚不存在,则以下方法将创建该文件共享。 该方法首先从连接字符串创建一个 ShareClient 对象。 然后,该示例会尝试下载我们先前创建的文件。 从 Main() 调用此方法。

//-------------------------------------------------
// Create a file share
//-------------------------------------------------
public async Task CreateShareAsync(string shareName)
{
    // Get the connection string from app settings
    string connectionString = ConfigurationManager.AppSettings["StorageConnectionString"];

    // Instantiate a ShareClient which will be used to create and manipulate the file share
    ShareClient share = new ShareClient(connectionString, shareName);

    // Create the share if it doesn't already exist
    await share.CreateIfNotExistsAsync();

    // Ensure that the share exists
    if (await share.ExistsAsync())
    {
        Console.WriteLine($"Share created: {share.Name}");

        // Get a reference to the sample directory
        ShareDirectoryClient directory = share.GetDirectoryClient("CustomLogs");

        // Create the directory if it doesn't already exist
        await directory.CreateIfNotExistsAsync();

        // Ensure that the directory exists
        if (await directory.ExistsAsync())
        {
            // Get a reference to a file object
            ShareFileClient file = directory.GetFileClient("Log1.txt");

            // Ensure that the file exists
            if (await file.ExistsAsync())
            {
                Console.WriteLine($"File exists: {file.Name}");

                // Download the file
                ShareFileDownloadInfo download = await file.DownloadAsync();

                // Save the data to a local file, overwrite if the file already exists
                using (FileStream stream = File.OpenWrite(@"downloadedLog1.txt"))
                {
                    await download.Content.CopyToAsync(stream);
                    await stream.FlushAsync();
                    stream.Close();

                    // Display where the file was saved
                    Console.WriteLine($"File downloaded: {stream.Name}");
                }
            }
        }
    }
    else
    {
        Console.WriteLine($"CreateShareAsync failed");
    }
}

设置文件共享的最大大小

从 Azure 文件存储客户端库的 5.x 版开始,可以设置文件共享的配额(最大大小)。 还可以查看共享当前存储了多少数据。

设置共享配额可以限制在该共享上存储的文件的总大小。 如果共享上的文件总大小超出了配额,则客户端无法增大现有文件的大小。 客户端也无法创建新文件,除非这些文件是空的。

下面的示例演示如何检查共享的当前使用情况,以及如何设置共享的配额。

//-------------------------------------------------
// Set the maximum size of a share
//-------------------------------------------------
public async Task SetMaxShareSizeAsync(string shareName, int increaseSizeInGiB)
{
    const long ONE_GIBIBYTE = 10737420000; // Number of bytes in 1 gibibyte

    // Get the connection string from app settings
    string connectionString = ConfigurationManager.AppSettings["StorageConnectionString"];

    // Instantiate a ShareClient which will be used to access the file share
    ShareClient share = new ShareClient(connectionString, shareName);

    // Create the share if it doesn't already exist
    await share.CreateIfNotExistsAsync();

    // Ensure that the share exists
    if (await share.ExistsAsync())
    {
        // Get and display current share quota
        ShareProperties properties = await share.GetPropertiesAsync();
        Console.WriteLine($"Current share quota: {properties.QuotaInGB} GiB");

        // Get and display current usage stats for the share
        ShareStatistics stats = await share.GetStatisticsAsync();
        Console.WriteLine($"Current share usage: {stats.ShareUsageInBytes} bytes");

        // Convert current usage from bytes into GiB
        int currentGiB = (int)(stats.ShareUsageInBytes / ONE_GIBIBYTE);

        // This line sets the quota to be the current 
        // usage of the share plus the increase amount
        await share.SetQuotaAsync(currentGiB + increaseSizeInGiB);

        // Get the new quota and display it
        properties = await share.GetPropertiesAsync();
        Console.WriteLine($"New share quota: {properties.QuotaInGB} GiB");
    }
}

为文件或文件共享生成共享访问签名

从 Azure 文件存储客户端库的 5.x 版开始,可以为文件共享或单个文件生成共享访问签名 (SAS)。

下面的示例方法返回指定共享中文件上的 SAS。

//-------------------------------------------------
// Create a SAS URI for a file
//-------------------------------------------------
public Uri GetFileSasUri(string shareName, string filePath, DateTime expiration, ShareFileSasPermissions permissions)
{
    // Get the account details from app settings
    string accountName = ConfigurationManager.AppSettings["StorageAccountName"];
    string accountKey = ConfigurationManager.AppSettings["StorageAccountKey"];

    ShareSasBuilder fileSAS = new ShareSasBuilder()
    {
        ShareName = shareName,
        FilePath = filePath,

        // Specify an Azure file resource
        Resource = "f",

        // Expires in 24 hours
        ExpiresOn = expiration
    };

    // Set the permissions for the SAS
    fileSAS.SetPermissions(permissions);

    // Create a SharedKeyCredential that we can use to sign the SAS token
    StorageSharedKeyCredential credential = new StorageSharedKeyCredential(accountName, accountKey);

    // Build a SAS URI
    UriBuilder fileSasUri = new UriBuilder($"https://{accountName}.file.core.chinacloudapi.cn/{fileSAS.ShareName}/{fileSAS.FilePath}");
    fileSasUri.Query = fileSAS.ToSasQueryParameters(credential).ToString();

    // Return the URI
    return fileSasUri.Uri;
}

若要详细了解如何创建和使用共享访问签名,请参阅共享访问签名的工作原理

复制文件

从 Azure 文件存储客户端库的 5.x 版开始,可以将一个文件复制到另一个文件,将一个文件复制到一个 Blob,或将一个 Blob 复制到一个文件。

还可以使用 AzCopy 将一个文件复制到另一个文件或将一个 Blob 复制到一个文件,反之亦然。 请参阅 AzCopy 入门

注意

如果将一个 Blob 复制到一个文件,或将一个文件复制到一个 Blob,必须使用共享访问签名 (SAS) 授予对源对象的访问权限,即使是在同一存储帐户内进行复制。

将一个文件复制到另一个文件

以下示例将一个文件复制到同一共享中的另一个文件。 可以使用共享密钥身份验证执行复制,因为此操作在同一存储帐户内复制文件。

//-------------------------------------------------
// Copy file within a directory
//-------------------------------------------------
public async Task CopyFileAsync(string shareName, string sourceFilePath, string destFilePath)
{
    // Get the connection string from app settings
    string connectionString = ConfigurationManager.AppSettings["StorageConnectionString"];

    // Get a reference to the file we created previously
    ShareFileClient sourceFile = new ShareFileClient(connectionString, shareName, sourceFilePath);

    // Ensure that the source file exists
    if (await sourceFile.ExistsAsync())
    {
        // Get a reference to the destination file
        ShareFileClient destFile = new ShareFileClient(connectionString, shareName, destFilePath);

        // Start the copy operation
        await destFile.StartCopyAsync(sourceFile.Uri);

        if (await destFile.ExistsAsync())
        {
            Console.WriteLine($"{sourceFile.Uri} copied to {destFile.Uri}");
        }
    }
}

将文件复制到 Blob

以下示例创建一个文件并将其复制到同一存储帐户中的某个 blob。 该示例为源文件创建一个 SAS,服务在复制操作期间使用该 SAS 授予对源文件的访问权限。

//-------------------------------------------------
// Copy a file from a share to a blob
//-------------------------------------------------
public async Task CopyFileToBlobAsync(string shareName, string sourceFilePath, string containerName, string blobName)
{
    // Get a file SAS from the method created ealier
    Uri fileSasUri = GetFileSasUri(shareName, sourceFilePath, DateTime.UtcNow.AddHours(24), ShareFileSasPermissions.Read);

    // Get a reference to the file we created previously
    ShareFileClient sourceFile = new ShareFileClient(fileSasUri);

    // Ensure that the source file exists
    if (await sourceFile.ExistsAsync())
    {
        // Get the connection string from app settings
        string connectionString = ConfigurationManager.AppSettings["StorageConnectionString"];

        // Get a reference to the destination container
        BlobContainerClient container = new BlobContainerClient(connectionString, containerName);

        // Create the container if it doesn't already exist
        await container.CreateIfNotExistsAsync();

        BlobClient destBlob = container.GetBlobClient(blobName);

        await destBlob.StartCopyFromUriAsync(sourceFile.Uri);

        if (await destBlob.ExistsAsync())
        {
            Console.WriteLine($"File {sourceFile.Name} copied to blob {destBlob.Name}");
        }
    }
}

可以用相同的方式将一个 Blob 复制到一个文件。 如果源对象是一个 Blob,则创建一个 SAS,以便在复制操作期间授予对该 Blob 的访问权限。

共享快照

从 Azure 文件存储客户端库的 8.5 版开始,可以创建共享快照。 还可以列出或浏览共享快照,以及删除共享快照。 创建后,共享快照是只读的。

创建共享快照

下面的示例创建文件共享快照。

//-------------------------------------------------
// Create a share snapshot
//-------------------------------------------------
public async Task CreateShareSnapshotAsync(string shareName)
{
    // Get the connection string from app settings
    string connectionString = ConfigurationManager.AppSettings["StorageConnectionString"];

    // Instatiate a ShareServiceClient
    ShareServiceClient shareServiceClient = new ShareServiceClient(connectionString);

    // Instantiate a ShareClient which will be used to access the file share
    ShareClient share = shareServiceClient.GetShareClient(shareName);

    // Ensure that the share exists
    if (await share.ExistsAsync())
    {
        // Create a snapshot
        ShareSnapshotInfo snapshotInfo = await share.CreateSnapshotAsync();
        Console.WriteLine($"Snapshot created: {snapshotInfo.Snapshot}");
    }
}

列出共享快照

以下示例列出共享上的快照。

//-------------------------------------------------
// List the snapshots on a share
//-------------------------------------------------
public void ListShareSnapshots()
{
    // Get the connection string from app settings
    string connectionString = ConfigurationManager.AppSettings["StorageConnectionString"];

    // Instatiate a ShareServiceClient
    ShareServiceClient shareServiceClient = new ShareServiceClient(connectionString);

    // Display each share and the snapshots on each share
    foreach (ShareItem item in shareServiceClient.GetShares(ShareTraits.All, ShareStates.Snapshots))
    {
        if (null != item.Snapshot)
        {
            Console.WriteLine($"Share: {item.Name}\tSnapshot: {item.Snapshot}");
        }
    }
}

列出共享快照中的文件和目录

以下示例浏览共享快照中的文件和目录。

//-------------------------------------------------
// List the snapshots on a share
//-------------------------------------------------
public void ListSnapshotContents(string shareName, string snapshotTime)
{
    // Get the connection string from app settings
    string connectionString = ConfigurationManager.AppSettings["StorageConnectionString"];

    // Instatiate a ShareServiceClient
    ShareServiceClient shareService = new ShareServiceClient(connectionString);

    // Get a ShareClient
    ShareClient share = shareService.GetShareClient(shareName);

    Console.WriteLine($"Share: {share.Name}");

    // Get as ShareClient that points to a snapshot
    ShareClient snapshot = share.WithSnapshot(snapshotTime);

    // Get the root directory in the snapshot share
    ShareDirectoryClient rootDir = snapshot.GetRootDirectoryClient();

    // Recursively list the directory tree
    ListDirTree(rootDir);
}

//-------------------------------------------------
// Recursively list a directory tree
//-------------------------------------------------
public void ListDirTree(ShareDirectoryClient dir)
{
    // List the files and directories in the snapshot
    foreach (ShareFileItem item in dir.GetFilesAndDirectories())
    {
        if (item.IsDirectory)
        {
            Console.WriteLine($"Directory: {item.Name}");
            ShareDirectoryClient subDir = dir.GetSubdirectoryClient(item.Name);
            ListDirTree(subDir);
        }
        else
        {
            Console.WriteLine($"File: {dir.Name}\\{item.Name}");
        }
    }
}

从共享快照还原文件共享或文件

创建文件共享的快照即可恢复各个文件或整个文件共享。

查询文件共享的共享快照即可从文件共享快照还原文件。 然后,可以检索属于特定共享快照的文件。 使用该版本直接读取或还原文件。

//-------------------------------------------------
// Restore file from snapshot
//-------------------------------------------------
public async Task RestoreFileFromSnapshot(string shareName, string directoryName, string fileName, string snapshotTime)
{
    // Get the connection string from app settings
    string connectionString = ConfigurationManager.AppSettings["StorageConnectionString"];

    // Instatiate a ShareServiceClient
    ShareServiceClient shareService = new ShareServiceClient(connectionString);

    // Get a ShareClient
    ShareClient share = shareService.GetShareClient(shareName);

    // Get as ShareClient that points to a snapshot
    ShareClient snapshot = share.WithSnapshot(snapshotTime);

    // Get a ShareDirectoryClient, then a ShareFileClient to the snapshot file
    ShareDirectoryClient snapshotDir = snapshot.GetDirectoryClient(directoryName);
    ShareFileClient snapshotFile = snapshotDir.GetFileClient(fileName);

    // Get a ShareDirectoryClient, then a ShareFileClient to the live file
    ShareDirectoryClient liveDir = share.GetDirectoryClient(directoryName);
    ShareFileClient liveFile = liveDir.GetFileClient(fileName);

    // Restore the file from the snapshot
    ShareFileCopyInfo copyInfo = await liveFile.StartCopyAsync(snapshotFile.Uri);

    // Display the status of the operation
    Console.WriteLine($"Restore status: {copyInfo.CopyStatus}");
}

删除共享快照

下面的示例删除文件共享快照。

//-------------------------------------------------
// Delete a snapshot
//-------------------------------------------------
public async Task DeleteSnapshotAsync(string shareName, string snapshotTime)
{
    // Get the connection string from app settings
    string connectionString = ConfigurationManager.AppSettings["StorageConnectionString"];

    // Instatiate a ShareServiceClient
    ShareServiceClient shareService = new ShareServiceClient(connectionString);

    // Get a ShareClient
    ShareClient share = shareService.GetShareClient(shareName);

    // Get a ShareClient that points to a snapshot
    ShareClient snapshotShare = share.WithSnapshot(snapshotTime);

    try
    {
        // Delete the snapshot
        await snapshotShare.DeleteIfExistsAsync();
    }
    catch (RequestFailedException ex)
    {
        Console.WriteLine($"Exception: {ex.Message}");
        Console.WriteLine($"Error code: {ex.Status}\t{ex.ErrorCode}");
    }
}

使用指标排查 Azure 文件存储问题

Azure 存储分析支持用于 Azure 文件存储的指标。 使用指标数据,可以跟踪请求和诊断问题。

可以通过 Azure 门户为 Azure 文件存储启用指标。 还可以通过 REST API 或 Azure 文件存储客户端库中的类似功能之一调用设置文件服务属性操作,以编程方式启用指标。

以下代码示例演示了如何使用 .NET 客户端库启用 Azure 文件存储的指标。

//-------------------------------------------------
// Use metrics
//-------------------------------------------------
public async Task UseMetricsAsync()
{
    // Get the connection string from app settings
    string connectionString = ConfigurationManager.AppSettings["StorageConnectionString"];

    // Instatiate a ShareServiceClient
    ShareServiceClient shareService = new ShareServiceClient(connectionString);

    // Set metrics properties for File service
    await shareService.SetPropertiesAsync(new ShareServiceProperties()
    {
        // Set hour metrics
        HourMetrics = new ShareMetrics()
        {
            Enabled = true,
            IncludeApis = true,
            Version = "1.0",

            RetentionPolicy = new ShareRetentionPolicy()
            {
                Enabled = true,
                Days = 14
            }
        },

        // Set minute metrics
        MinuteMetrics = new ShareMetrics()
        {
            Enabled = true,
            IncludeApis = true,
            Version = "1.0",

            RetentionPolicy = new ShareRetentionPolicy()
            {
                Enabled = true,
                Days = 7
            }
        }
    });

    // Read the metrics properties we just set
    ShareServiceProperties serviceProperties = await shareService.GetPropertiesAsync();

    // Display the properties
    Console.WriteLine();
    Console.WriteLine($"HourMetrics.InludeApis: {serviceProperties.HourMetrics.IncludeApis}");
    Console.WriteLine($"HourMetrics.RetentionPolicy.Days: {serviceProperties.HourMetrics.RetentionPolicy.Days}");
    Console.WriteLine($"HourMetrics.Version: {serviceProperties.HourMetrics.Version}");
    Console.WriteLine();
    Console.WriteLine($"MinuteMetrics.InludeApis: {serviceProperties.MinuteMetrics.IncludeApis}");
    Console.WriteLine($"MinuteMetrics.RetentionPolicy.Days: {serviceProperties.MinuteMetrics.RetentionPolicy.Days}");
    Console.WriteLine($"MinuteMetrics.Version: {serviceProperties.MinuteMetrics.Version}");
    Console.WriteLine();
}

如果遇到任何问题,请参阅排查 Azure 文件存储问题

后续步骤

有关 Azure 文件存储的详细信息,请参阅以下资源:

概念性文章

文件存储的工具支持

参考

有关使用已弃用的 .NET 版本 11.x SDK 的相关代码示例,请参阅使用 .NET 版本 11.x 的代码示例