Azure Blob 存储时间戳

azure-storage-blob-ts
分类编程
作者Agentic Awesome Skills 社区
许可MIT
评分4.90/5
使用14.5K

@azure/storage-blob (TypeScript/JavaScript)

用于 Azure Blob Storage 操作的 SDK —— 支持上传、下载、列出以及管理 Blob 和容器。

安装

bash
npm install @azure/storage-blob @azure/identity

当前版本: 12.x
Node.js: >= 18.0.0

环境变量

bash
AZURE_STORAGE_ACCOUNT_NAME=<account-name>
AZURE_STORAGE_ACCOUNT_KEY=<account-key>

或连接字符串

AZURE_STORAGE_CONNECTION_STRING=DefaultEndpointsProtocol=https;AccountName=...

身份验证

DefaultAzureCredential (推荐)

typescript
import { BlobServiceClient } from "@azure/storage-blob";
import { DefaultAzureCredential } from "@azure/identity";

const accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME!;
const client = new BlobServiceClient(
https://${accountName}.blob.core.windows.net,
new DefaultAzureCredential()
);

连接字符串

typescript
import { BlobServiceClient } from "@azure/storage-blob";

const client = BlobServiceClient.fromConnectionString(
process.env.AZURE_STORAGE_CONNECTION_STRING!
);

StorageSharedKeyCredential (仅限 Node.js)

typescript
import { BlobServiceClient, StorageSharedKeyCredential } from "@azure/storage-blob";

const accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME!;
const accountKey = process.env.AZURE_STORAGE_ACCOUNT_KEY!;

const sharedKeyCredential = new StorageSharedKeyCredential(accountName, accountKey);
const client = new BlobServiceClient(
https://${accountName}.blob.core.windows.net,
sharedKeyCredential
);

SAS 令牌

typescript
import { BlobServiceClient } from "@azure/storage-blob";

const accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME!;
const sasToken = process.env.AZURE_STORAGE_SAS_TOKEN!; // 以 "?" 开头

const client = new BlobServiceClient(
https://${accountName}.blob.core.windows.net${sasToken}
);

客户端层级结构

code
BlobServiceClient (账户级)
└── ContainerClient (容器级)
    └── BlobClient (Blob 级)
        ├── BlockBlobClient (块 Blob - 最常用)
        ├── AppendBlobClient (追加 Blob)
        └── PageBlobClient (页 Blob - VHDs)

容器操作

创建容器

typescript
const containerClient = client.getContainerClient("my-container");
await containerClient.create();

// 或在不存在时创建
await containerClient.createIfNotExists();

列出容器

typescript
for await (const container of client.listContainers()) {
  console.log(container.name);
}

// 使用前缀过滤
for await (const container of client.listContainers({ prefix: "logs-" })) {
console.log(container.name);
}

删除容器

typescript
await containerClient.delete();
// 或在存在时删除
await containerClient.deleteIfExists();

Blob 操作

上传 Blob (简单上传)

typescript
const containerClient = client.getContainerClient("my-container");
const blockBlobClient = containerClient.getBlockBlobClient("my-file.txt");

// 上传字符串
await blockBlobClient.upload("Hello, World!", 13);

// 上传 Buffer
const buffer = Buffer.from("Hello, World!");
await blockBlobClient.upload(buffer, buffer.length);

从文件上传 (仅限 Node.js)

typescript
const blockBlobClient = containerClient.getBlockBlobClient("uploaded-file.txt");
await blockBlobClient.uploadFile("/path/to/local/file.txt");

从流上传 (仅限 Node.js)

typescript
import * as fs from "fs";

const blockBlobClient = containerClient.getBlockBlobClient("streamed-file.txt");
const readStream = fs.createReadStream("/path/to/local/file.txt");

await blockBlobClient.uploadStream(readStream, 4 * 1024 * 1024, 5, {
// bufferSize: 4MB, maxConcurrency: 5
onProgress: (progress) => console.log(已上传 ${progress.loadedBytes} 字节),
});

从浏览器上传

typescript
const blockBlobClient = containerClient.getBlockBlobClient("browser-upload.txt");

// 从 File 输入框上传
const fileInput = document.getElementById("fileInput") as HTMLInputElement;
const file = fileInput.files![0];
await blockBlobClient.uploadData(file);

// 从 Blob/ArrayBuffer 上传
const arrayBuffer = new ArrayBuffer(1024);
await blockBlobClient.uploadData(arrayBuffer);

下载 Blob

typescript
const blobClient = containerClient.getBlobClient("my-file.txt");
const downloadResponse = await blobClient.download();

// 读取为字符串 (浏览器 & Node.js)
const downloaded = await streamToText(downloadResponse.readableStreamBody!);

async function streamToText(readable: NodeJS.ReadableStream): Promise<string> {
const chunks: Buffer[] = [];
for await (const chunk of readable) {
chunks.push(Buffer.from(chunk));
}
return Buffer.concat(chunks).toString("utf-8");
}

下载到文件 (仅限 Node.js)

typescript
const blockBlobClient = containerClient.getBlockBlobClient("my-file.txt");
await blockBlobClient.downloadToFile("/path/to/local/destination.txt");

下载到 Buffer (仅限 Node.js)

typescript
const blockBlobClient = containerClient.getBlockBlobClient("my-file.txt");
const buffer = await blockBlobClient.downloadToBuffer();
console.log(buffer.toString());

列出 Blobs

typescript
// 列出所有 blobs
for await (const blob of containerClient.listBlobsFlat()) {
  console.log(blob.name, blob.properties.contentLength);
}

// 按前缀列出
for await (const blob of containerClient.listBlobsFlat({ prefix: "logs/" })) {
console.log(blob.name);
}

// 按层级列出 (虚拟目录)
for await (const item of containerClient.listBlobsByHierarchy("/")) {
if (item.kind === "prefix") {
console.log(目录: ${item.name});
} else {
console.log(Blob: ${item.name});
}
}

删除 Blob

typescript
const blobClient = containerClient.getBlobClient("my-file.txt");
await blobClient.delete();

// 如果存在则删除
await blobClient.deleteIfExists();

// 删除及其快照
await blobClient.delete({ deleteSnapshots: "include" });

复制 Blob

typescript
const sourceBlobClient = containerClient.getBlobClient("source.txt");
const destBlobClient = containerClient.getBlobClient("destination.txt");

// 开始复制操作
const copyPoller = await destBlobClient.beginCopyFromURL(sourceBlobClient.url);
await copyPoller.pollUntilDone();

Blob 属性与元数据

获取属性

typescript
const blobClient = containerClient.getBlobClient("my-file.txt");
const properties = await blobClient.getProperties();

console.log("Content-Type:", properties.contentType);
console.log("Content-Length:", properties.contentLength);
console.log("Last Modified:", properties.lastModified);
console.log("ETag:", properties.etag);

设置元数据

types
typescript await blobClient.setMetadata({ author: "John Doe", category: "documents", });
code
### 设置 HTTP 标头
typescript await blobClient.setHTTPHeaders({ blobContentType: "text/plain", blobCacheControl: "max-age=3600", blobContentDisposition: "attachment; filename=download.txt", });
code
## SAS 令牌生成 (仅限 Node.js)

生成 Blob SAS

typescript import { BlobSASPermissions, generateBlobSASQueryParameters, StorageSharedKeyCredential, } from "@azure/storage-blob";

const sharedKeyCredential = new StorageSharedKeyCredential(accountName, accountKey);

const sasToken = generateBlobSASQueryParameters(
{
containerName: "my-container",
blobName: "my-file.txt",
permissions: BlobSASPermissions.parse("r"), // 只读
startsOn: new Date(),
expiresOn: new Date(Date.now() + 3600 * 1000), // 1 小时
},
sharedKeyCredential
).toString();

const sasUrl = https://${accountName}.blob.core.windows.net/my-container/my-file.txt?${sasToken};

code
### 生成容器 SAS
typescript
import { ContainerSASPermissions, generateBlobSASQueryParameters } from "@azure/storage-blob";

const sasToken = generateBlobSASQueryParameters(
{
containerName: "my-container",
permissions: ContainerSASPermissions.parse("racwdl"), // 读取、添加、创建、写入、删除、列出
expiresOn: new Date(Date.now() + 24 * 3600 * 1000), // 24 小时
},
sharedKeyCredential
).toString();

code
### 生成账户 SAS
typescript
import {
AccountSASPermissions,
AccountSASResourceTypes,
AccountSASServices,
generateAccountSASQueryParameters,
} from "@azure/storage-blob";

const sasToken = generateAccountSASQueryParameters(
{
services: AccountSASServices.parse("b").toString(), // blob
resourceTypes: AccountSASResourceTypes.parse("sco").toString(), // 服务、容器、对象
permissions: AccountSASPermissions.parse("rwdlacupi"), // 所有权限
expiresOn: new Date(Date.now() + 24 * 3600 * 1000),
},
sharedKeyCredential
).toString();

code
## Blob 类型

块 Blob (Block Blob - 默认)

最常用的文本和二进制文件类型。

typescript
const blockBlobClient = containerClient.getBlockBlobClient("document.pdf");
await blockBlobClient.uploadFile("/path/to/document.pdf");
code
### 追加 Blob (Append Blob)

针对追加操作进行了优化(如日志、审计跟踪)。

typescript
const appendBlobClient = containerClient.getAppendBlobClient("app.log");

// 创建追加 Blob
await appendBlobClient.create();

// 追加数据
await appendBlobClient.appendBlock("Log entry 1\n", 12);
await appendBlobClient.appendBlock("Log entry 2\n", 12);

code
### 页 Blob (Page Blob)

用于随机读写的固定大小 Blob(如 VHD)。

typescript
const pageBlobClient = containerClient.getPageBlobClient("disk.vhd");

// 创建 512 字节对齐的页 Blob
await pageBlobClient.create(1024 * 1024); // 1MB

// 写入页(必须 512 字节对齐)
const buffer = Buffer.alloc(512);
await pageBlobClient.uploadPages(buffer, 0, 512);

code
## 错误处理
typescript
import { RestError } from "@azure/storage-blob";

try {
await containerClient.create();
} catch (error) {
if (error instanceof RestError) {
switch (error.statusCode) {
case 404:
console.log("未找到容器");
break;
case 409:
console.log("容器已存在");
break;
case 403:
console.log("访问被拒绝");
break;
default:
console.error(存储错误 ${error.statusCode}:);

code
${error.message});
}
}
throw error;
}

TypeScript 类型参考

typescript
import {
  // 客户端
  BlobServiceClient,
  ContainerClient,
  BlobClient,
  BlockBlobClient,
  AppendBlobClient,
  PageBlobClient,

// 身份验证
StorageSharedKeyCredential,
AnonymousCredential,

// SAS
BlobSASPermissions,
ContainerSASPermissions,
AccountSASPermissions,
AccountSASServices,
AccountSASResourceTypes,
generateBlobSASQueryParameters,
generateAccountSASQueryParameters,

// 选项与响应
BlobDownloadResponseParsed,
BlobUploadCommonResponse,
ContainerCreateResponse,
BlobItem,
ContainerItem,

// 错误
RestError,
} from "@azure/storage-blob";

最佳实践

1. 使用 DefaultAzureCredential — 优先使用 AAD 而非连接字符串/密钥
2. 大文件使用流式传输 — 文件 > 256MB 时使用
uploadStream/downloadToFile
3. 设置正确的内容类型 — 使用
setHTTPHeaders 设置正确的 MIME 类型
4. 客户端访问使用 SAS 令牌 — 为浏览器上传生成短效令牌
5. 优雅地处理错误 — 检查
RestError.statusCode 进行针对性处理
6. **使用
*IfNotExists 方法 — 实现容器/Blob 创建的幂等性
7.
关闭客户端** — 虽然非强制,但在长期运行的应用中是良好的习惯

平台差异

| 功能 | Node.js | 浏览器 |
|---------|---------|---------|
|
StorageSharedKeyCredential | ✅ | ❌ |
|
uploadFile() | ✅ | ❌ |
|
uploadStream() | ✅ | ❌ |
|
downloadToFile() | ✅ | ❌ |
|
downloadToBuffer() | ✅ | ❌ |
|
uploadData()` | ✅ | ✅ |
| SAS 生成 | ✅ | ❌ |
| DefaultAzureCredential | ✅ | ❌ |
| 匿名/SAS 访问 | ✅ | ✅ |

适用场景

本技能适用于执行概览中所描述的工作流或操作。

局限性

  • 仅在任务明确符合上述范围时使用此技能。
  • 不要将输出视为环境特定验证、测试或专家评审的替代方案。
  • 如果缺失必要输入、权限、安全边界或成功标准,请停止并请求澄清。