如何为Azure Blob实现IDataSource抽象类的cwd等方法
实现Azure Blob版IDataSource子类
前置准备
首先安装Azure Blob存储官方SDK:
npm install @azure/storage-blob
类型定义(适配抽象类的File类型)
先补充抽象类依赖的File接口(如果项目中未定义):
interface File { name: string; size: number; type: string; isDirectory?: boolean; }
完整实现代码
import { BlobServiceClient, ContainerClient, AzureReadableStream } from '@azure/storage-blob'; import { Logger } from './your-logger-path'; // 替换为项目中Logger的实际路径 import { IDataSource } from './your-idatasource-path'; // 替换为IDataSource的实际路径 import { Readable, Writable } from 'stream'; export class AzureBlobDataSource extends IDataSource { private containerClient: ContainerClient; constructor( logger: Logger, debug: boolean, connectionString: string, containerName: string ) { super(logger, debug); // 初始化Azure Blob容器客户端 const blobServiceClient = BlobServiceClient.fromConnectionString(connectionString); this.containerClient = blobServiceClient.getContainerClient(containerName); } // 列出指定路径下的文件/虚拟目录(Blob用前缀模拟目录) async list(path: string): Promise<File[]> { const prefix = path ? `${path.replace(/\/$/, '')}/` : ''; const files: File[] = []; const seenDirs = new Set<string>(); for await (const blob of this.containerClient.listBlobsFlat({ prefix })) { const relativePath = blob.name.slice(prefix.length); const dirSplitIndex = relativePath.indexOf('/'); // 处理虚拟目录 if (dirSplitIndex !== -1) { const dirName = relativePath.slice(0, dirSplitIndex); const dirKey = `${prefix}${dirName}`; if (!seenDirs.has(dirKey)) { seenDirs.add(dirKey); files.push({ name: dirName, size: 0, type: 'directory', isDirectory: true }); } } else { // 处理Blob文件 files.push({ name: relativePath, size: blob.properties.contentLength || 0, type: blob.properties.contentType || 'application/octet-stream', isDirectory: false }); } } return files; } // 读取Blob内容到指定写入流 async read(path: string, writeStream: NodeJS.WritableStream): Promise<void> { const blobClient = this.containerClient.getBlockBlobClient(path); await blobClient.downloadToWritableStream(writeStream); } // 上传读取流内容到Blob async write(path: string, readStream: NodeJS.ReadableStream): Promise<void> { const blobClient = this.containerClient.getBlockBlobClient(path); await blobClient.uploadStream(readStream); } // 创建Blob的可读流(转换Azure Web Stream为Node.js Stream) async createReadStream(path: string): Promise<Readable> { const blobClient = this.containerClient.getBlockBlobClient(path); const downloadRes = await blobClient.download(); return Readable.from(downloadRes.readableStreamBody as AzureReadableStream<Uint8Array>); } // 创建Blob的可写流 async createWriteStream(path: string): Promise<Writable> { const blobClient = this.containerClient.getBlockBlobClient(path); return blobClient.createWriteStream(); } // Blob客户端无需显式关闭,空实现兼容接口 async close(): Promise<void> { if (this.debug) { this.logger.info('Azure Blob连接已关闭(无实际操作)'); } } // 判断路径是否存在:区分文件/虚拟目录 async exists(path: string, isFile: boolean): Promise<boolean> { if (isFile) { const blobClient = this.containerClient.getBlockBlobClient(path); return blobClient.exists(); } else { const prefix = `${path.replace(/\/$/, '')}/`; const iterator = this.containerClient.listBlobsFlat({ prefix, maxResults: 1 }); const result = await iterator.next(); return !result.done; } } // 删除指定Blob文件 async delete(path: string): Promise<void> { const blobClient = this.containerClient.getBlockBlobClient(path); await blobClient.deleteIfExists(); } // 删除虚拟目录(删除前缀下所有Blob) async removeDir(path: string): Promise<void> { const prefix = `${path.replace(/\/$/, '')}/`; const deleteTasks: Promise<void>[] = []; for await (const blob of this.containerClient.listBlobsFlat({ prefix })) { const blobClient = this.containerClient.getBlockBlobClient(blob.name); deleteTasks.push(blobClient.deleteIfExists()); } await Promise.all(deleteTasks); } // 创建虚拟目录:生成空占位Blob标识目录(可选) async makeDir(path: string, recursive: boolean): Promise<void> { const dirPath = `${path.replace(/\/$/, '')}/`; const blobClient = this.containerClient.getBlockBlobClient(dirPath); await blobClient.upload('', 0); } // 返回当前工作目录(Blob以容器为根,返回容器名) async cwd(): Promise<string> { return this.containerClient.containerName; } }
关键说明
- 虚拟目录处理:Azure Blob是对象存储,无真实目录结构,所有目录通过Blob名称前缀模拟,list/exists/removeDir等方法均基于前缀逻辑实现。
- 流转换:Azure SDK返回的是Web标准流,需转换为Node.js原生流才能适配抽象类接口。
- 配置替换:代码中
your-logger-path和your-idatasource-path需替换为项目内实际文件路径;构造函数的connectionString和containerName需从Azure门户获取。
内容的提问来源于stack exchange,提问作者Ashish Khokhariya
相关产品推荐
相关产品推荐

