如何通过 Node.js 使用 Blob 存储
发布于 3 个月前 作者 Azcommunity 847 次浏览 来自 分享

概述

本文介绍如何使用 Blob 存储执行常见方案。 相关示例是通过 Node.js API 编写的。 涉及的方案包括如何上传、列出、下载和删除 Blob。

什么是 Blob 存储

Azure Blob 存储是用于存储大量非结构化对象数据(例如文本或二进制数据)的服务,这些数据可通过 HTTP 或 HTTPS 从世界各地进行访问。 你可以使用 Blob 存储向外公开数据,或者私下存储应用程序数据。 Blob 存储的常见用途包括:

  • 直接向浏览器提供图像或文档
  • 存储文件以供分布式访问
  • 对视频和音频进行流式处理
  • 存储数据以用于备份和还原、灾难恢复及存档
  • 存储数据以供本地或 Azure 托管服务执行分析

Blob 服务概念

Blob 服务包含以下组件: blob1.png 存储帐户: 对 Azure 存储服务的所有访问都要通过存储帐户来完成。 此存储帐户可以是常规用途存储帐户,也可以是专用于存储对象/Blob 的 Blob 存储帐户。 有关详细信息,请参阅关于 Azure 存储帐户。 容器: 一个容器包含一组 blob 集。 所有 blob 必须位于相应的容器中。 一个帐户可以包含无限个容器。 一个容器可以存储无限个 Blob。 请注意,容器名称必须小写。 Blob: 任何类型和大小的文件。 Azure 存储提供三种类型的 Blob:块 Blob、页 Blob 和追加 Blob。 块 Blob 特别适用于存储短的文本或二进制文件,例如文档和媒体文件。 追加 Blob 类似于块 Blob,因为它们是由块组成的,但针对追加操作对它们进行了优化,因此它们适用于日志记录方案。 单个块 Blob 可以包含最多 50000 个块,每个块最大 100 MB,总大小稍微大于 4.75 TB (100 MB X 50000)。 单个追加 Blob 可以包含最多 50000 个块,每个块最大 4 MB,总大小稍微大于 195 GB (4 MB X 50000)。 页 Blob 最大可达 1 TB 大小,并且对于频繁的读/写操作更加高效。 Azure 虚拟机使用页 Blob 作为 OS 和数据磁盘。 有关命名容器和 Blob 的详细信息,请参阅 命名和引用容器、Blob 和元数据。

创建 Azure 存储帐户

创建第一个 Azure 存储帐户的最简单方法是使用 Azure 门户。 若要了解更多信息,请参阅 创建存储帐户。 还可使用 Azure PowerShell、Azure CLI 或适用于 .NET 的存储资源提供程序客户端库创建 Azure 存储帐户。 如果暂时不想创建存储帐户,也可以使用 Azure 存储模拟器在本地环境中运行和测试代码。 有关详细信息,请参阅 使用 Azure 存储模拟器进行开发和测试。 创建 Node.js 应用程序 有关如何创建 Node.js 应用程序的说明,请参阅在 Azure App Service 中创建 Node.js Web 应用、 Node.js 应用程序并将其部署到 Azure 云服务或使用 Web Matrix 构建 Node.js Web 应用并将其部署到 Azure。

配置应用程序以访问存储

若要使用 Azure 存储,需要 Azure Storage SDK for Node.js,其中包括一组便于与存储 REST 服务进行通信的库。 使用 Node 包管理器 (NPM) 可获取该程序包 使用命令行接口(如 PowerShell (Windows)、Terminal (Mac) 或 Bash (Unix))导航到在其中创建示例应用程序的文件夹。 在命令窗口中键入 npm install azure-storage 。 该命令的输出类似于以下代码示例。 azure-storage@0.5.0 node_modules\azure-storage ±- extend@1.2.1 ±- xmlbuilder@0.4.3 ±- mime@1.2.11 ±- node-uuid@1.4.3 ±- validator@3.22.2 ±- underscore@1.4.4 ±- readable-stream@1.0.33 (string_decoder@0.10.31, isarray@0.0.1, inherits@2.0.1, core-util-is@1.0.1) ±- xml2js@0.2.7 (sax@0.5.2) ±- request@2.57.0 (caseless@0.10.0, aws-sign2@0.5.0, forever-agent@0.6.1, stringstream@0.0.4, oauth-sign@0.8.0, tunnel-agent@0.4.1, isstream@0.1.2, json-stringify-safe@5.0.1, bl@0.9.4, combined-stream@1.0.5, qs@3.1.0, mime-types@2.0.14, form-data@0.2.0, http-signature@0.11.0, tough-cookie@2.0.0, hawk@2.3.1, har-validator@1.8.0) 可以手动运行 ls 命令来验证是否创建了 node_modules 文件夹。 在该文件夹中,将找到 azure-storage 包,其中包含访问存储所需的库。

导入包

使用记事本或其他文本编辑器将以下内容添加到要在其中使用存储的应用程序的 server.js 文件的顶部: var azure = require('azure-storage'); Azure 存储中的每个 Blob 必须驻留在一个容器中。 该容器构成 Blob 名称的一部分。 例如,在这些示例 Blob URI 中,mycontainer 是容器的名称: https://storagesample.blob.core.chinacloudapi.cn/mycontainer/blob1.txt https://storagesample.blob.core.chinacloudapi.cn/mycontainer/photos/myphoto.jpg 容器名称必须是有效的 DNS 名称,并符合以下命名规则:

  1. 容器名称必须以字母或数字开头,并且只能包含字母、数字和短划线 (-) 字符。
  2. 每个短划线 (-) 字符的前面和后面都必须是一个字母或数字;在容器名称中不允许连续的短划线 (-)。
  3. 容器名称中的所有字母都必须为小写。
  4. 容器名称必须介于 3 到 63 个字符。

Important 请注意,容器的名称必须始终为小写。 如果你在容器名称中包括大写字母或以其他方式违反了容器命名规则,则可能会收到 400 错误(错误请求)。

若要创建一个新的容器,请使用 createContainerIfNotExists。 以下代码示例将创建名为“mycontainer”的新容器: blobSvc.createContainerIfNotExists('mycontainer', {publicAccessLevel : 'blob'}, function(error, result, response){ if(!error){ // Container exists and allows // anonymous read access to blob // content and metadata within this container } }); 另外,可以通过使用 setContainerAcl 指定访问级别来修改容器的访问级别。 以下代码示例将访问级别更改为“容器”: blobSvc.setContainerAcl('mycontainer', null /* signedIdentifiers */, {publicAccessLevel : 'container'} /* publicAccessLevel*/, function(error, result, response){ if(!error){ // Container access level set to 'container' } }); 结果将包含有关操作的信息,包括容器的当前 ETag 。

筛选器

可以向使用 BlobService 执行的操作应用可选的筛选操作。 筛选操作可包括日志记录、自动重试等。筛选器是实现具有签名的方法的对象: function handle (requestOptions, next) 在对请求选项执行预处理后,该方法需要调用“next”并且传递具有以下签名的回调: function (returnObject, finalCallback, next) 在此回调中并且在处理 returnObject(来自对服务器请求的响应)后,回调需要调用 next(如果存在)以继续处理其他筛选器或只调用 finalCallback 以便结束服务调用。 Azure SDK for Node.js 中附带了两个实现了重试逻辑的筛选器,分别是 ExponentialRetryPolicyFilter 和 LinearRetryPolicyFilter。 下面的代码将创建一个 BlobService 对象,该对象使用 ExponentialRetryPolicyFilter: var retryOperations = new azure.ExponentialRetryPolicyFilter(); var blobSvc = azure.createBlobService().withFilter(retryOperations);

将 Blob 上传到容器中

有三种类型的 Blob:块 Blob、页 Blob 和追加 Blob。 块 Blob 能够实现更高效地上传大型数据。 追加 Blob 针对追加操作进行了优化。 页 Blob 针对读取/写入操作进行了优化。 有关详细信息,请参阅 Understanding Block Blobs, Append Blobs, and Page Blobs(了解块 Blob、追加 Blob 和页 Blob)。 块 Blob 若要将数据上传到块 Blob,可使用以下方法:

  • createBlockBlobFromLocalFile - 创建新的块 Blob 并上传文件的内容
  • createBlockBlobFromStream - 创建新的块 Blob 并上传流的内容
  • createBlockBlobFromText - 创建新的块 Blob 并上传字符串的内容
  • createWriteStreamToBlockBlob - 向块 Blob 提供写入流

下面的代码示例会将 test.txt 文件的内容上传到 myblob 中。 blobSvc.createBlockBlobFromLocalFile('mycontainer', 'myblob', 'test.txt', function(error, result, response){ if(!error){ // file uploaded } }); 这些方法返回的 result 将包含有关操作的信息,例如 Blob 的 ETag 。

追加 Blob

若要将数据上传到新的追加 Blob,可使用以下方法:

  • createAppendBlobFromLocalFile - 创建新的追加 Blob 并上传文件的内容
  • createAppendBlobFromStream - 创建新的追加 Blob 并上传流的内容
  • createAppendBlobFromText - 创建新的追加 Blob 并上传字符串的内容
  • createWriteStreamToNewAppendBlob - 创建新的追加 blob,然后向其提供要写入的流

以下代码示例会将 test.txt 文件的内容上传到 myappendblob 中。 blobSvc.createAppendBlobFromLocalFile('mycontainer', 'myappendblob', 'test.txt', function(error, result, response){ if(!error){ // file uploaded } }); 若要将块追加到现有追加 Blob,请使用以下方法:

  • appendFromLocalFile - 将文件的内容追加到现有追加 Blob
  • appendFromStream - 将流的内容追加到现有追加 Blob
  • appendFromText - 将字符串的内容追加到现有追加 Blob
  • appendBlockFromStream - 将流的内容追加到现有追加 Blob
  • appendBlockFromText - 将字符串的内容追加到现有追加 Blob

以下代码示例会将 test.txt 文件的内容上传到 myappendblob 中。 blobSvc.appendFromText('mycontainer', 'myappendblob', 'text to be appended', function(error, result, response){ if(!error){ // text appended } });

页 Blob

若要将数据上传到页 Blob,可使用以下方法:

  • createPageBlob - 创建新的特定长度的页 Blob
  • createPageBlobFromLocalFile - 创建新的页 Blob 并上传文件的内容
  • createPageBlobFromStream - 创建新的页 Blob 并上传流的内容
  • createWriteStreamToExistingPageBlob - 向现有页 Blob 提供写入流
  • createWriteStreamToNewPageBlob - 创建新的页 Blob,然后向其提供要写入的流

以下代码示例会将 test.txt 文件的内容上传到 mypageblob 中。 blobSvc.createPageBlobFromLocalFile('mycontainer', 'mypageblob', 'test.txt', function(error, result, response){ if(!error){ // file uploaded } }); Note 页 Blob 包含 512 字节的“页面”。 如果上传大小不是 512 倍数的数据,则会收到错误。

列出容器中的 Blob

若要列出容器中的 Blob,请使用 listBlobsSegmented 方法。 如果想要返回带特定前缀的 Blob,请使用 listBlobsSegmentedWithPrefix。 blobSvc.listBlobsSegmented('mycontainer', null, function(error, result, response){ if(!error){ // result.entries contains the entries // If not all blobs were returned, result.continuationToken has the continuation token. } }); result 包含一个 entries 集合,该集合是一组用于描述每个 blob 的对象。 如果不能返回所有 blob,result 还将提供 continuationToken,这可用作第二个参数来检索其他条目。

下载 Blob

若要从 Blob 下载数据,可使用以下方法:

  • getBlobToLocalFile - 将 Blob 内容写入文件
  • getBlobToStream - 将 Blob 内容写入流
  • getBlobToText - 将 Blob 内容写入字符串
  • createReadStream - 提供可从 Blob 读取内容的流

以下代码示例演示如何使用 getBlobToStream 下载 myblob blob 的内容,并使用一个流将其存储到 output.txt 文件: var fs = require('fs'); blobSvc.getBlobToStream('mycontainer', 'myblob', fs.createWriteStream('output.txt'), function(error, result, response){ if(!error){ // blob retrieved } }); result 包含有关 Blob 的信息,包括 ETag 信息。

删除 Blob

最后,若要删除 Blob,请调用 deleteBlob。 以下代码示例将删除名为 myblob的 Blob。 blobSvc.deleteBlob(containerName, 'myblob', function(error, response){ if(!error){ // Blob has been deleted } });

并发访问

若要允许从多个客户端或多个进程实例并发访问某个 blob,可以使用 ETag 或租用。

  • Etag - 用于检测 Blob 或容器是否已被其他进程修改
  • 租约 - 用于在某个时段内获取对 Blob 的独占式可续订写入或删除访问

ETag

如果需要允许多个客户端或实例同时写入块 Blob 或页 Blob,请使用 ETag。 ETag 用于确定自第一次读取或创建某个容器或 Blob 以来,该容器或 Blob 是否被修改,这样就可以避免覆盖其他客户端或进程提交的更改。 可以使用可选的 options.accessConditions 参数设置 ETag 条件。 如果 blob 已存在且具有 etagToMatch 所包含的 ETag 值,以下代码示例将仅上传 test.txt 文件。 blobSvc.createBlockBlobFromLocalFile('mycontainer', 'myblob', 'test.txt', { accessConditions: { EtagMatch: etagToMatch} }, function(error, result, response){ if(!error){ // file uploaded } }); 当使用 ETag 时,常规模式为:

  1. 通过创建、列出或获取操作来获取 ETag。
  2. 执行一个操作,查看 ETag 值是否尚未修改。 如果值已修改,则表明在获得 ETag 值后,其他客户端或实例已修改该 Blob 或容器。

租约

新的租约可使用 acquireLease 方法获取,只需指定希望获取其租约的 Blob 或容器即可。 例如,以下代码将获取 myblob的租约。 blobSvc.acquireLease('mycontainer', 'myblob', function(error, result, response){ if(!error) { console.log('leaseId: ' + result.id); } }); 对 myblob 的后续操作必须提供 options.leaseId 参数。 租约 ID 作为 result.id 从 acquireLease返回。

Note 默认情况下,租约期限为无期。 可以指定一个有限的租期(15 到 60 秒),只需提供 options.leaseDuration 参数即可。

若要删除租约,请使用 releaseLease。 若要中断租约,但又要防止其他人在原始租约到期之前获得新租约,则可使用 breakLease。

使用共享访问签名

共享访问签名 (SAS) 是一种安全的方法,用于对 blob 和容器进行细致访问而无需提供存储帐户名或密钥。 通常使用共享访问签名来提供对数据的有限访问权限,例如允许移动应用程序访问 Blob。

Note 虽然也可以允许匿名访问 Blob,但共享访问签名可以允许提供更受控制的访问,因为必须生成 SAS。

受信任的应用程序(例如基于云的服务)使用 BlobService 的 generateSharedAccessSignature 生成共享访问签名,然后将其提供给不受信任的或不完全受信任的应用程序,例如移动应用。 共享访问签名可使用策略生成,该策略描述了共享访问签名的生效日期和失效日期,以及授予共享访问签名持有者的访问级别。 以下代码示例生成了一个新的共享访问策略,该策略将允许共享访问签名持有者对 myblob Blob 执行读取操作,并且在创建后 100 分钟过期。 var startDate = new Date(); var expiryDate = new Date(startDate); expiryDate.setMinutes(startDate.getMinutes() + 100); startDate.setMinutes(startDate.getMinutes() - 100);

var sharedAccessPolicy = { AccessPolicy: { Permissions: azure.BlobUtilities.SharedAccessPermissions.READ, Start: startDate, Expiry: expiryDate }, };

var blobSAS = blobSvc.generateSharedAccessSignature('mycontainer', 'myblob', sharedAccessPolicy); var host = blobSvc.host;

请注意,还必须提供主机信息,因为共享访问签名持有者尝试访问容器时,必须提供该信息。 然后,客户端应用程序将共享访问签名用于 BlobServiceWithSAS ,以便针对 Blob 执行操作。 以下语句获取有关 myblob的信息。 var sharedBlobSvc = azure.createBlobServiceWithSas(host, blobSAS); sharedBlobSvc.getBlobProperties('mycontainer', 'myblob', function (error, result, response) { if(!error) { // retrieved info } });

由于共享访问签名在生成时具有只读访问权限,因此如果尝试修改 Blob,则会返回错误。

访问控制列表

还可以使用访问控制列表 (ACL) 为 SAS 设置访问策略。 如果希望允许多个客户端访问某个容器,但为每个客户端提供了不同的访问策略,则访问控制列表会很有用。 ACL 是使用一组访问策略实施的,每个策略都有一个关联的 ID。 以下代码示例定义了两个策略,一个用于“user1”,一个用于“user2”: var sharedAccessPolicy = { user1: { Permissions: azure.BlobUtilities.SharedAccessPermissions.READ, Start: startDate, Expiry: expiryDate }, user2: { Permissions: azure.BlobUtilities.SharedAccessPermissions.WRITE, Start: startDate, Expiry: expiryDate } }; 以下代码示例将获取 mycontainer 的当前 ACL,然后使用 setBlobAcl 添加新策略。 此方法具有以下用途: var extend = require('extend'); blobSvc.getBlobAcl('mycontainer', function(error, result, response) { if(!error){ var newSignedIdentifiers = extend(true, result.signedIdentifiers, sharedAccessPolicy); blobSvc.setBlobAcl('mycontainer', newSignedIdentifiers, function(error, result, response){ if(!error){ // ACL set } }); } }); 设置 ACL 后,可以根据某个策略的 ID 创建共享访问签名。 以下代码示例将为“user2”创建新的共享访问签名: blobSAS = blobSvc.generateSharedAccessSignature('mycontainer', { Id: 'user2' });

后续步骤

有关详细信息,请参阅以下资源。

  • Azure Storage SDK for Node API 参考
  • Azure 存储团队博客
  • Azure Storage SDK for Node 存储库
  • Node.js 开发人员中心
  • 使用 AzCopy 命令行实用程序传输数据

更多Node.JS精彩干货 请点击前往查看

欢迎有兴趣的朋友多多交流 A究院研究生 Azurecommunity@qq.com

回到顶部