export type MediaCacheIndex =UTSJSONObject type DownloadFileResult = { statusCode?: number tempFilePath?: string tempFilePathArray?: string[] apFilePath?: string // UTS不支持索引签名,已移除 } export class MediaCacheService { static STORAGE_KEY: string = 'media-cache-index' static index: MediaCacheIndex | null = null static async init(): Promise { if (this.index != null) return try { const res = await new Promise((resolve) => { uni.getStorage({ key: this.STORAGE_KEY, success: (result) => { const json = result.data as UTSJSONObject const data = json['data'] as UTSJSONObject | null resolve(data) }, fail: () => resolve(null) }) }) if (res != null) { this.index = res } else { this.index = {} } } catch (_) { this.index = {} } } static async persist(): Promise { try { // @ts-ignore await uni.setStorage({ key: this.STORAGE_KEY, data: this.index ?? {} }) } catch (_) {} } static async getCachedPath(url: string): Promise { await this.init() if (this.index == null) return url const index = this.index! const existing = index[url] as string | null if (existing != null && await this.fileExists(existing)) { return existing } const saved = await this.downloadAndSave(url) if (saved != null) { this.index[url] = saved await this.persist() return saved } // fallback to remote url if save failed return url } static async clear(url?: string): Promise { await this.init() if (this.index == null) return if (url != null && url != '') { this.index[url] = null } else { this.index = {} } await this.persist() } private static async fileExists(filePath: string): Promise { return await new Promise((resolve) => { try { const fs = uni.getFileSystemManager() let settled = false const finish = (value: boolean) => { if (settled) { return } settled = true resolve(value) } fs.access({ path: filePath, success: () => finish(true), fail: () => finish(false) }) } catch (_) { resolve(false) } }) } private static async downloadAndSave(url: string): Promise { const dlRes = await new Promise((resolve) => { try { uni.downloadFile({ url, success: (res) => { const result: DownloadFileResult = { statusCode: res.statusCode, tempFilePath: res['tempFilePath'] as string | null, tempFilePathArray: res['tempFilePathArray'] as string[] | null, apFilePath: res['apFilePath'] as string | null } resolve(result) }, fail: () => resolve(null) }) } catch (_) { resolve(null) } }) const statusCode = dlRes?.statusCode if (dlRes == null || statusCode == null || statusCode < 200 || statusCode >= 400) { return null } const tempFilePath = dlRes.tempFilePath ?? (dlRes.tempFilePathArray != null ? dlRes.tempFilePathArray[0] : null) ?? dlRes.apFilePath ?? null if (tempFilePath == null) return null const savedPath = await new Promise((resolve) => { try { const fs = uni.getFileSystemManager() let settled = false const finish = (value: string | null) => { if (settled) { return } settled = true resolve(value) } const options: SaveFileOptions = { tempFilePath: tempFilePath, success: (res) => { const savedPath = res.savedFilePath if (savedPath != null && savedPath.length > 0) { finish(savedPath) return } finish(null) }, fail: () => finish(null) } fs.saveFile(options as SaveFileOptions) } catch (_) { resolve(null) } }) if (savedPath != null && savedPath.length > 0) { return savedPath } return null } } export default MediaCacheService