Compartir a través de

开始使用 Azure Blob 存储和 TypeScript

本文介绍如何使用适用于 TypeScript 的 Azure Blob 存储客户端库连接到 Azure Blob 存储。 连接后,可以针对 Blob 存储服务的容器、Blob 和功能运行代码。

包 (npm) | API 参考 | 库源代码 |

先决条件

设置项目

  1. 请打开命令提示符并更改为你的项目文件夹。 将 YOUR-DIRECTORY 更改为你的文件夹名称:

    cd YOUR-DIRECTORY
    
  2. 如果目录中没有 package.json 文件,请初始化项目以创建该文件:

    npm init -y
    
  3. 安装包含 TypeScript 类型的 TypeScript 和适用于 JavaScript 的 Azure Blob 存储 客户端库:

    npm install typescript @azure/storage-blob
    
  4. 如果要通过 Microsoft Entra ID 使用无密码连接,请安装适用于 JavaScript 的 Azure 标识客户端库:

    npm install @azure/identity
    

授予访问权限并连接到 Blob 存储

Microsoft Entra ID 通过管理连接标识(托管标识)提供最安全的连接。 此无密码功能使你可以开发不需要存储在代码中的任何机密(密钥或连接字符串)的应用程序。

设置对 Azure 云的标识访问权限

若要在没有密码的情况下连接到 Azure,需要设置 Azure 标识或使用现有标识。 设置标识后,请确保为标识分配相应的角色。

若要使用 Microsoft Entra ID 授权无密码访问,需要使用 Azure 凭据。 需要哪种类型的凭据取决于应用程序的运行位置。 使用此表作为指南。

环境 方法
开发人员环境 Visual Studio Code
开发人员环境 服务主体
Azure 托管的应用 Azure 托管的应用设置
本地 本地应用设置

设置存储帐户角色

你的存储资源需要将以下一个或多个 Azure RBAC 角色分配给你计划连接的标识资源。 为在上一步中创建的每个标识设置 Azure 存储角色:Azure 云、本地开发、本地。

完成设置后,每个标识至少需要其中一个相应角色:

  • 数据访问角色 - 例如:

    • 存储 Blob 数据读者
    • 存储 Blob 数据参与者
  • 资源角色 - 例如:

    • 读者
    • 参与者

生成应用程序

在生成应用程序时,代码主要与三种类型的资源交互:

  • 存储帐户,它是 Azure 存储数据的唯一顶级命名空间。
  • 容器,用于组织存储帐户中的 Blob 数据。
  • Blob,用于存储非结构化数据,例如文本和二进制数据。

以下图示显示了这些资源之间的关系。

Blob 存储体系结构的示意图

每种类型的资源由一个或多个关联的 JavaScript 客户端表示:

说明
BlobServiceClient 表示存储帐户的 Blob 存储终结点。
ContainerClient 允许操作 Azure 存储容器及其 Blob。
BlobClient 允许操作 Azure 存储 Blob。

创建 BlobServiceClient 对象

BlobServiceClient 对象是 SDK 中的顶级对象。 此客户端允许操作服务、容器和 Blob。

设置 Azure 存储帐户标识角色和本地环境后,创建包含 @azure/identity 包的 TypeScript 文件。 创建凭据(例如 DefaultAzureCredential),以实现与 Blob 存储的无密码连接。 使用该凭据通过 BlobServiceClient 对象进行身份验证。

// connect-with-default-azure-credential.js
// You must set up RBAC for your identity with one of the following roles:
// - Storage Blob Data Reader
// - Storage Blob Data Contributor
import { DefaultAzureCredential } from '@azure/identity';
import { BlobServiceClient } from '@azure/storage-blob';
import * as dotenv from 'dotenv';
dotenv.config();

const accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME as string;
if (!accountName) throw Error('Azure Storage accountName not found');

const blobServiceClient = new BlobServiceClient(
  `https://${accountName}.blob.core.chinacloudapi.cn`,
  new DefaultAzureCredential()
);

async function main() {
  const containerName = 'my-container';
  const blobName = 'my-blob';

  const timestamp = Date.now();
  const fileName = `my-new-file-${timestamp}.txt`;

  // create container client
  const containerClient = await blobServiceClient.getContainerClient(
    containerName
  );

  // create blob client
  const blobClient = await containerClient.getBlockBlobClient(blobName);

  // download file
  const downloadResult = await blobClient.downloadToFile(fileName);

  if (downloadResult.errorCode) throw Error(downloadResult.errorCode);

  console.log(
    `${fileName} downloaded ${downloadResult.contentType}, isCurrentVersion: ${downloadResult.isCurrentVersion}`
  );
}

main()
  .then(() => console.log(`success`))
  .catch((err: unknown) => {
    if (err instanceof Error) {
      console.log(err.message);
    }
  });

dotenv 包用于从 .env 文件中读取存储帐户名称。 不应将此文件签入到源代码管理中。 如果使用本地服务主体作为 DefaultAzureCredential 设置的一部分,该凭据的任何安全信息也将进入 .env 文件。

如果你打算将应用程序部署到在 Azure 外部运行的服务器和客户端,请创建符合需要的凭据之一。

创建 ContainerClient 对象

可以从 BlobServiceClient 创建 ContainerClient 对象,或直接创建。

从 BlobServiceClient 创建 ContainerClient 对象

从 BlobServiceClient 创建 ContainerClient 对象。

// Azure Storage dependency
import {
  BlobServiceClient,
  StorageSharedKeyCredential
} from '@azure/storage-blob';

// For development environment - include environment variables from .env
import * as dotenv from 'dotenv';
dotenv.config();

// Azure Storage resource name
const accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME as string;
if (!accountName) throw Error('Azure Storage accountName not found');

// Azure Storage resource key
const accountKey = process.env.AZURE_STORAGE_ACCOUNT_KEY as string;
if (!accountKey) throw Error('Azure Storage accountKey not found');

// Create credential
const sharedKeyCredential = new StorageSharedKeyCredential(
  accountName,
  accountKey
);

const baseUrl = `https://${accountName}.blob.core.chinacloudapi.cn`;
const containerName = `my-container`;

// Create BlobServiceClient
const blobServiceClient = new BlobServiceClient(
  `${baseUrl}`,
  sharedKeyCredential
);

async function main(): Promise<void> {
  try {
    // Create container client
    const containerClient = await blobServiceClient.getContainerClient(
      containerName
    );

    // do something with containerClient...
    let i = 1;

    // List blobs in container
    for await (const blob of containerClient.listBlobsFlat({
      includeMetadata: true,
      includeSnapshots: false,
      includeTags: true,
      includeVersions: false,
      prefix: ''
    })) {
      console.log(`Blob ${i++}: ${blob.name}`);
    }
  } catch (err) {
    console.log(err);
    throw err;
  }
}

main()
  .then(() => console.log(`success`))
  .catch((err: unknown) => {
    if (err instanceof Error) {
      console.log(err.message);
    }
  });

直接创建 ContainerClient

// Azure Storage dependency
import { ContainerClient } from '@azure/storage-blob';

// Azure authentication for credential dependency
import { DefaultAzureCredential } from '@azure/identity';

// For development environment - include environment variables from .env
import * as dotenv from 'dotenv';
dotenv.config();

// Azure Storage resource name
const accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME as string;
if (!accountName) throw Error('Azure Storage accountName not found');

// Azure SDK needs base URL
const baseUrl = `https://${accountName}.blob.core.chinacloudapi.cn`;

// Unique container name
const timeStamp = Date.now();
const containerName = `my-container`;

async function main(): Promise<void> {
  try {
    // create container client from DefaultAzureCredential
    const containerClient = new ContainerClient(
      `${baseUrl}/${containerName}`,
      new DefaultAzureCredential()
    );

    // do something with containerClient...
    let i = 1;

    // List blobs in container
    for await (const blob of containerClient.listBlobsFlat({
      includeMetadata: true,
      includeSnapshots: false,
      includeTags: true,
      includeVersions: false,
      prefix: ''
    })) {
      console.log(`Blob ${i++}: ${blob.name}`);
    }
  } catch (err) {
    console.log(err);
    throw err;
  }
}

main()
  .then(() => console.log(`success`))
  .catch((err: unknown) => {
    if (err instanceof Error) {
      console.log(err.message);
    }
  });

dotenv 包用于从 .env 文件中读取存储帐户名称。 不应将此文件签入到源代码管理中。

创建 BlobClient 对象

可以从 ContainerClient 创建下面列出的任何 BlobClient 对象,或直接创建。

Blob 客户端列表:

从 ContainerClient 创建 BlobClient 对象

// Azure Storage dependency
import {
  BlobClient,
  BlobDownloadHeaders,
  BlobGetPropertiesHeaders,
  BlobGetPropertiesResponse,
  BlockBlobClient,
  ContainerClient
} from '@azure/storage-blob';

// For development environment - include environment variables from .env
import * as dotenv from 'dotenv';
import { getContainerClientFromDefaultAzureCredential } from './auth-get-client';
dotenv.config();

const containerName = `my-container`;
const containerClient: ContainerClient =
  getContainerClientFromDefaultAzureCredential(containerName);

const blobName = `my-blob`;

async function main(containerClient: ContainerClient): Promise<void> {
  // Create BlobClient object
  const blobClient: BlobClient = containerClient.getBlobClient(blobName);

  // do something with blobClient...
  const properties: BlobGetPropertiesHeaders = await blobClient.getProperties();
  if (properties.errorCode) throw Error(properties.errorCode);

  console.log(`Blob ${blobName} properties:`);

  // get BlockBlobClient from blobClient
  const blockBlobClient: BlockBlobClient = blobClient.getBlockBlobClient();

  // do something with blockBlobClient...
  const downloadResponse: BlobDownloadHeaders = await blockBlobClient.download(
    0
  );
  if (downloadResponse.errorCode) throw Error(downloadResponse.errorCode);
}

main(containerClient)
  .then(() => console.log(`success`))
  .catch((err: unknown) => {
    if (err instanceof Error) {
      console.log(err.message);
    }
  });

直接创建 BlobClient

// Azure Storage dependency
import {
  BlockBlobClient,
  BlockBlobUploadHeaders,
  BlockBlobUploadResponse
} from '@azure/storage-blob';
import { getBlockBlobClientFromDefaultAzureCredential } from './auth-get-client';

// For development environment - include environment variables from .env
import * as dotenv from 'dotenv';
dotenv.config();

// Container must exist prior to running this script
const containerName = `my-container`;

// Random blob name and contents
const timeStamp = Date.now();
const blobName = `${timeStamp}-my-blob.txt`;
const fileContentsAsString = 'Hello there.';

const blockBlobClient: BlockBlobClient =
  getBlockBlobClientFromDefaultAzureCredential(containerName, blobName);

async function main(
  blockBlobClient: BlockBlobClient
): Promise<BlockBlobUploadHeaders> {
  // Get file url - available before contents are uploaded
  console.log(`blob.url: ${blockBlobClient.url}`);

  // Upload file contents
  const result: BlockBlobUploadHeaders = await blockBlobClient.upload(
    fileContentsAsString,
    fileContentsAsString.length
  );

  if (result.errorCode) throw Error(result.errorCode);

  // Get results
  return result;
}

main(blockBlobClient)
  .then((result) => {
    console.log(result);
    console.log(`success`);
  })
  .catch((err: unknown) => {
    if (err instanceof Error) {
      console.log(err.message);
    }
  });

/*

Response looks like this:

{
  etag: '"0x8DAD247F1F4896E"',
  lastModified: 2022-11-29T20:26:07.000Z,
  contentMD5: <Buffer 9d 6a 29 63 87 20 77 db 67 4a 27 a3 9c 49 2e 61>,
  clientRequestId: 'a07fdd1f-5937-44c7-984f-0699a48a05c0',
  requestId: '3580e726-201e-0045-1a30-0474f6000000',
  version: '2021-04-10',
  date: 2022-11-29T20:26:06.000Z,
  isServerEncrypted: true,
  'content-length': '0',
  server: 'Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0',
  'x-ms-content-crc64': 'BLv7vb1ONT8=',
  body: undefined
}
*/

dotenv 包用于从 .env 文件中读取存储帐户名称。 不应将此文件签入到源代码管理中。

另请参阅