从 Xamarin.Forms 中存储和访问 Azure 存储中的数据

Azure 存储是一种可缩放的云存储解决方案,可用于存储非结构化数据和结构化数据。 本文演示了如何使用 Xamarin.Forms 在 Azure 存储中存储文本和二进制数据,以及如何访问数据。

Azure 存储提供四种存储服务:

  • Blob 存储。 Blob 可以是文本或二进制数据,例如备份、虚拟机、媒体文件或文档。
  • 表存储是 NoSQL 密钥属性存储。
  • 队列存储是一种消息传递服务,用于在云服务之间进行工作流处理和通信。
  • 文件存储使用 SMB 协议提供共享存储。

有两种类型的存储帐户:

  • 常规用途存储帐户提供从单个帐户访问 Azure 存储服务的权限。
  • Blob 存储帐户是专用于存储 Blob 的存储帐户。 建议仅当需要存储 Blob 数据时才使用此帐户类型。

本文和随附的示例应用程序演示了如何将图像和文本文件上传到 Blob 存储以及下载它们。 此外,它还演示了从 Blob 存储检索文件列表以及删除文件。

有关 Azure 存储的详细信息,请参阅存储简介

注意

如果还没有 Azure 订阅,可以在开始前创建一个免费帐户

Blob 存储简介

Blob 存储由三个组件组成,如下图所示:

Blob 存储概念

对 Azure 存储的所有访问都通过存储帐户。 一个存储帐户可以包含无限数量的容器,一个容器可以存储无限数量的 Blob,直至达到存储帐户的容量限制。

Blob 是任何类型和大小的文件。 Azure 存储支持三种不同的 Blob 类型:

  • 块 Blob 进行了相应的优化来流化和存储云对象,并且是用于存储备份、介质文件、文档等对象的不错选择。块 Blob 的大小最大为 195Gb。
  • 追加 Blob 类似于块 Blob,但针对追加追加操作进行了优化,如日志记录。 追加 Blob 的大小最大为 195Gb。
  • 页 Blob 针对频繁的读/写操作进行优化,通常用于存储虚拟机及其磁盘。 页 Blob 的大小最大为 1Tb。

注意

请注意,Blob 存储帐户支持块 blob 和追加 Blob,但不支持页 Blob。

Blob 作为字节流上传到 Azure 存储并从 Azure 存储下载。 因此,文件必须在上传之前转换为字节流,并在下载后转换回其原始表示形式。

存储在 Azure 存储中的每个对象都具有唯一的 URL 地址。 存储帐户名称构成了该地址的子域,子域和域名的组合构成了存储帐户的终结点。 例如,如果存储帐户命名 mystorageaccount,则存储帐户的默认 Blob 终结点为 https://mystorageaccount.blob.core.windows.net

用于访问存储帐户中某个对象的 URL 是通过将存储帐户中对象的位置附加到终结点而构建的。 例如,Blob 地址的格式为 https://mystorageaccount.blob.core.windows.net/mycontainer/myblob

安装

将 Azure 存储帐户集成到 Xamarin.Forms 应用程序的过程如下所示:

  1. 创建存储帐户。 有关详细信息,请参阅创建存储帐户
  2. Azure 存储客户端库添加到 Xamarin.Forms 应用程序。
  3. 配置存储空间连接字符串。 有关详细信息,请参阅连接到 Azure 存储
  4. Microsoft.WindowsAzure.StorageMicrosoft.WindowsAzure.Storage.Blob 命名空间的 using 指令添加到将要访问 Azure 存储的类。

连接到 Azure 存储

必须对存储帐户资源发出的每个请求进行身份验证。 虽然可以将 Blob 配置为支持匿名身份验证,但应用程序可以使用两种主要方法通过存储帐户进行身份验证:

  • 共享密钥。 此方法使用 Azure 存储帐户名称和帐户密钥来访问存储服务。 存储帐户在创建时分配了两个私钥,可用于共享密钥身份验证。
  • 共享访问签名。 这是一个可以附加到 URL 上的令牌,它能在其有效期限内,启用对存储资源的委托访问,并赋予其所指定的权限。

可以指定连接字符串,其中包括从应用程序访问 Azure 存储资源所需的身份验证信息。 此外,还可以将连接字符串配置为从 Visual Studio 连接到 Azure 存储模拟器。

注意

Azure 存储支持连接字符串中的 HTTP 和 HTTPS。 但是,建议使用 HTTPS。

连接到 Azure 存储模拟器

Azure 存储模拟器提供了一个模拟 Azure Blob、队列和表服务以进行开发的本地环境。

应使用以下连接字符串连接到 Azure 存储模拟器:

UseDevelopmentStorage=true

有关 Azure 存储模拟器的详细信息,请参阅使用 Azure 存储模拟器进行开发和测试

使用共享密钥连接到 Azure 存储

应使用以下连接字符串格式通过共享密钥连接到 Azure 存储:

DefaultEndpointsProtocol=[http|https];AccountName=myAccountName;AccountKey=myAccountKey

myAccountName 应替换为存储帐户的名称,myAccountKey 应替换为两个帐户访问密钥之一。

注意

使用共享密钥身份验证时,帐户名称和帐户密钥将被分发给使用应用程序的每个人,这将对存储帐户提供完全的读写访问权限。 因此,仅出于测试目的使用共享密钥身份验证,并且永远不会将密钥分发给其他用户。

使用共享访问签名连接到 Azure 存储

应使用以下连接字符串格式通过 SAS 连接到 Azure 存储:

BlobEndpoint=myBlobEndpoint;SharedAccessSignature=mySharedAccessSignature

myBlobEndpoint 应替换为 Blob 终结点的 URL,mySharedAccessSignature 应替换为 SAS。 SAS 提供用于访问资源的协议、服务终结点和凭据。

注意

建议对生产应用程序使用 SAS 身份验证。 但是,在生产应用程序中,应从后端服务按需检索 SAS,而不是与应用程序捆绑在一起。

有关共享访问签名的详细信息,请参阅使用共享访问签名 (SAS)

创建容器

GetContainer 方法用于检索对命名容器的引用,然后可用于从容器中检索 Blob 或将 Blob 添加到容器。 下面的代码示例说明 GetContainer 方法:

static CloudBlobContainer GetContainer(ContainerType containerType)
{
  var account = CloudStorageAccount.Parse(Constants.StorageConnection);
  var client = account.CreateCloudBlobClient();
  return client.GetContainerReference(containerType.ToString().ToLower());
}

CloudStorageAccount.Parse 方法分析连接字符串,并返回表示存储帐户的 CloudStorageAccount 实例。 CloudBlobClient 实例,用于检索容器和 Blob,然后由 CreateCloudBlobClient 方法创建。 GetContainerReference 方法在将指定的容器作为 CloudBlobContainer 实例返回给调用方法之前,会先检索它。 在此示例中,容器名称是 ContainerType 枚举值,转换为小写字符串。

注意

容器名称必须为小写,并且必须以字母或数字开头。 此外,它们只能包含字母、数字和短划线字符,并且长度必须介于 3 到 63 个字符之间。

调用 GetContainer 方法,如下所示:

var container = GetContainer(containerType);

然后,如果容器尚不存在,可以使用 CloudBlobContainer 实例来创建容器:

await container.CreateIfNotExistsAsync();

默认情况下,新建的容器是专用的。 这意味着必须指定存储访问密钥才能从容器中检索 Blob。 有关在容器中公开 Blob 的信息,请参阅创建容器

将数据上传到容器

UploadFileAsync 方法用于将字节数据流上传到 Blob 存储,并显示在以下代码示例中:

public static async Task<string> UploadFileAsync(ContainerType containerType, Stream stream)
{
  var container = GetContainer(containerType);
  await container.CreateIfNotExistsAsync();

  var name = Guid.NewGuid().ToString();
  var fileBlob = container.GetBlockBlobReference(name);
  await fileBlob.UploadFromStreamAsync(stream);

  return name;
}

检索容器引用后,如果容器尚不存在,该方法将创建容器。 然后创建一个新的 Guid 以充当唯一的 Blob 名称,并将 Blob 块引用检索为 CloudBlockBlob 实例。 然后,使用 UploadFromStreamAsync 方法将数据流上传到 Blob,该方法将创建 Blob(如果尚不存在)或者覆盖它(如果存在)。

必须先将文件转换为字节流,然后才能使用此方法将文件上传到 Blob 存储。 以下代码示例对此进行了演示:

var byteData = Encoding.UTF8.GetBytes(text);
uploadedFilename = await AzureStorage.UploadFileAsync(ContainerType.Text, new MemoryStream(byteData));

text 数据将转换为字节数组,然后包装为传递给 UploadFileAsync 方法的流。

从容器下载数据

GetFileAsync 方法用于从 Azure 存储下载 Blob 数据,并显示在以下代码示例中:

public static async Task<byte[]> GetFileAsync(ContainerType containerType, string name)
{
  var container = GetContainer(containerType);

  var blob = container.GetBlobReference(name);
  if (await blob.ExistsAsync())
  {
    await blob.FetchAttributesAsync();
    byte[] blobBytes = new byte[blob.Properties.Length];

    await blob.DownloadToByteArrayAsync(blobBytes, 0);
    return blobBytes;
  }
  return null;
}

检索容器引用后,该方法将检索存储数据的 Blob 引用。 如果 Blob 存在,则其属性由 FetchAttributesAsync 方法检索。 将创建大小正确的字节数组,并将 Blob 下载为返回调用方法的字节数组。

下载 Blob 字节数据后,必须将其转换为其原始表示形式。 以下代码示例对此进行了演示:

var byteData = await AzureStorage.GetFileAsync(ContainerType.Text, uploadedFilename);
string text = Encoding.UTF8.GetString(byteData);

字节数组由 GetFileAsync 方法从 Azure 存储中检索,然后再将其转换回 UTF8 编码字符串。

列出容器中的数据

GetFilesListAsync 方法用于检索存储在容器中的 Blob 列表,并显示在以下代码示例中:

public static async Task<IList<string>> GetFilesListAsync(ContainerType containerType)
{
  var container = GetContainer(containerType);

  var allBlobsList = new List<string>();
  BlobContinuationToken token = null;

  do
  {
    var result = await container.ListBlobsSegmentedAsync(token);
    if (result.Results.Count() > 0)
    {
      var blobs = result.Results.Cast<CloudBlockBlob>().Select(b => b.Name);
      allBlobsList.AddRange(blobs);
    }
    token = result.ContinuationToken;
  } while (token != null);

  return allBlobsList;
}

检索容器引用后,该方法使用容器的 ListBlobsSegmentedAsync 方法检索对容器中 Blob 的引用。 当 BlobContinuationToken 实例不是 null 时,将枚举 ListBlobsSegmentedAsync 方法返回的结果。 将每个 Blob 从返回的 IListBlobItem 强制转换为 CloudBlockBlob,以便访问 Blob 的 Name 属性,然后再将其值添加到 allBlobsList 集合中。 当 BlobContinuationToken 实例为 null 后,返回最后一个 Blob 名称,并且执行退出循环。

从容器中删除数据

DeleteFileAsync 方法用于从容器中删除 Blob,并显示在以下代码示例中:

public static async Task<bool> DeleteFileAsync(ContainerType containerType, string name)
{
  var container = GetContainer(containerType);
  var blob = container.GetBlobReference(name);
  return await blob.DeleteIfExistsAsync();
}

检索容器引用后,该方法将检索指定 Blob 的 Blob 引用。 然后,使用 DeleteIfExistsAsync 方法删除 Blob。