import fs from 'node:fs/promises' import path from 'node:path' import type {KvStore} from './Types' import type {Upload} from '../models' /** * 文件键值存储(FileKvStore) * * @description 基于文件系统的键值对存储实现,专门用于存储上传文件的元数据 * @remarks * - 将上传文件的JSON元数据存储在磁盘上,与上传文件同目录 * - 使用队列机制确保并发安全,每次仅处理一个操作 * * @typeparam T 存储的数据类型,默认为Upload类型 */ export class FileKvStore implements KvStore { /** 存储目录路径 */ directory: string /** * 构造函数 * * @param path 指定存储元数据的目录路径 */ constructor(path: string) { this.directory = path } /** * 根据键获取存储的值 * * @param key 键名 * @returns 返回对应的值,如果不存在则返回undefined */ async get(key: string): Promise { try { // 读取对应键的JSON文件 const buffer = await fs.readFile(this.resolve(key), 'utf8') // 解析JSON并返回 return JSON.parse(buffer as string) } catch { // 文件不存在或读取失败时返回undefined return undefined } } /** * 存储键值对 * @param key 键名 * @param value 要存储的值 */ async set(key: string, value: T): Promise { // 将值转换为JSON并写入文件 await fs.writeFile(this.resolve(key), JSON.stringify(value)) } /** * 删除指定键的值 * * @param key 要删除的键名 */ async delete(key: string): Promise { // 删除对应的JSON文件 await fs.rm(this.resolve(key)) } /** * 列出所有存储的键 * * @returns 返回已存储的键名数组 */ async list(): Promise> { // 读取目录中的所有文件 const files = await fs.readdir(this.directory) // 对文件名进行排序 const sorted = files.sort((a, b) => a.localeCompare(b)) // 提取文件名(不包含扩展名) const name = (file: string) => path.basename(file, '.json') // 过滤出有效的tus文件ID // 仅保留成对出现的文件(文件名相同,一个有.json扩展名) return sorted.filter( (file, idx) => idx < sorted.length - 1 && name(file) === name(sorted[idx + 1]) ) } /** * 将键转换为完整的文件路径 * * @param key 键名 * @returns 返回完整的文件路径 * @private */ private resolve(key: string): string { // 将键名转换为完整的JSON文件路径 return path.resolve(this.directory, `${key}.json`) } }