UNPKG

key-cache

Version:

Storing data in the form of key into the file

231 lines (191 loc) 5.7 kB
/** * @file key-cache * @author xiaowu * @email fe.xiaowu@gmail.com */ import {statSync, writeFileSync, readdirSync} from 'fs'; import {mkdirsSync, emptyDirSync, readJsonSync, removeSync} from 'fs-extra'; import {resolve} from 'path'; import {createHash} from 'crypto'; /** * 检查文件是否存在 * * @inner * @param {string} filePath 文件路径 * @param {string} [type=file] 检测类型 * * @return {boolean} 是否存在 */ let exists = (filePath, type = 'file') => { try { return type === 'file' ? statSync(filePath).isFile() : statSync(filePath).isDirectory(); } catch (e) { return false; } }; export default class KeyCache { static options = { /** * 缓存目录,路径基于当前运行目录 * * @default 安装包里 dirname + .cache * @type {String} */ dir: resolve(__dirname, '..', '.cache'), /** * 保存的时间,单位秒,如果是null则表示一直保存 * * @default null * @type {Number|null} */ timeout: null, /** * 是否使用md5命名缓存文件 * * @default true * @type {Boolean} */ md5key: true } constructor(options = {}) { // 合并默认配置 this.options = {...KeyCache.options, ...options}; // 解析路径 this.options.dir = resolve(this.options.dir); // 创建缓存目录 mkdirsSync(this.options.dir); } /** * 设置缓存 * * @param {string} key key * @param {Object|string} value 值 * @param {Object|undefined} options 可选配置 * @return {Object} this */ set(key, value, options = {}) { if (!key || 'string' !== typeof key || value === undefined) { return this; } options = {...this.options, ...options}; let data = { __time: null, __result: value, __key: key }; // 如果有过期时间则记录,否则忽略 if (options.timeout !== null) { data.__time = Date.now(); data.__end = options.timeout; } // 如果配置的缓存目录不是初始的则创建该目录,防止出错 if (options.dir !== this.options.dir) { mkdirsSync(options.dir); } // 写入缓存 writeFileSync(this._getFilePath(key, options), JSON.stringify(data)); return this; } /** * 获取缓存文件路径 * * @private * @param {string} key key * @param {Object|undefined} options 配置参数,如果为空则调用this.options * * @return {string} 文件绝对路径 */ _getFilePath(key, options) { let filename; options = options || this.options; if (options.md5key) { let md5 = createHash('md5'); // unescape encodeURIComponent 兼容中文 md5 md5.update(unescape(encodeURIComponent(key))); filename = md5.digest('hex'); } else { // 把文件名中的:替换为空 filename = String(key).replace(/[^\u4e00-\u9fa5a-zA-Z\_\-0-9]/g, ''); // 如果替换后为空则使用md5 if (!filename) { return this._getFilePath(key, {...options, ...{md5key: true}}); } } return resolve(options.dir, filename + '.json'); } /** * 获取所有缓存数据 * * @return {Object} {key: value, ...} */ getAll() { let dir = resolve(this.options.dir); // 如果目录不存在 if (!exists(dir, 'dir')) { return {}; } let data = {}; readdirSync(dir).map(filename => { let result = readJsonSync(resolve(dir, filename), { throws: false }) || {}; if (result.__key) { data[result.__key] = this.get(result.__key); } }); return data; } /** * 获取缓存 * * @param {string} key key * * @return {Object|string|null} 结果,如果没有找到或者过期则为null */ get(key) { let filepath = this._getFilePath(key); // 如果文件不存在 if (!exists(filepath)) { return null; } let filedata = readJsonSync(filepath, { throws: false }) || {}; // 如果没有时间就不是key-cache设置的,则忽略 if (filedata.__time === undefined) { return null; } // 如果设置了时间并且过期了 // 则删除这个缓存文件 if (filedata.__time !== null && Date.now() - filedata.__time > filedata.__end * 1000) { this.remove(key); return null; } // 如果为undefined则返回null,因为值是fn时不会被JSON.stringify解析 return filedata.__result !== undefined ? filedata.__result : null; } /** * 删除缓存 * * @param {string|undefined} key key,如果为空则全部删除 * * @return {Object} this */ remove(key) { // 如果没有key则说明全部删除 if (!key) { emptyDirSync(this.options.dir); return this; } let filepath = this._getFilePath(key); // 如果文件不存在 if (!exists(filepath)) { return this; } // 删除文件 removeSync(filepath); return this; } }