94 lines
2.6 KiB
TypeScript
94 lines
2.6 KiB
TypeScript
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<T = Upload> implements KvStore<T> {
|
||
/** 存储目录路径 */
|
||
directory: string
|
||
/**
|
||
* 构造函数
|
||
*
|
||
* @param path 指定存储元数据的目录路径
|
||
*/
|
||
constructor(path: string) {
|
||
this.directory = path
|
||
}
|
||
/**
|
||
* 根据键获取存储的值
|
||
*
|
||
* @param key 键名
|
||
* @returns 返回对应的值,如果不存在则返回undefined
|
||
*/
|
||
async get(key: string): Promise<T | undefined> {
|
||
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<void> {
|
||
// 将值转换为JSON并写入文件
|
||
await fs.writeFile(this.resolve(key), JSON.stringify(value))
|
||
}
|
||
/**
|
||
* 删除指定键的值
|
||
*
|
||
* @param key 要删除的键名
|
||
*/
|
||
async delete(key: string): Promise<void> {
|
||
// 删除对应的JSON文件
|
||
await fs.rm(this.resolve(key))
|
||
}
|
||
|
||
/**
|
||
* 列出所有存储的键
|
||
*
|
||
* @returns 返回已存储的键名数组
|
||
*/
|
||
async list(): Promise<Array<string>> {
|
||
// 读取目录中的所有文件
|
||
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`)
|
||
}
|
||
} |