|
|
@@ -68,6 +68,7 @@ interface ProcessedImageInfo {
|
|
|
fileName: string;
|
|
|
memoryUri: string;
|
|
|
fd: number;
|
|
|
+ sourceHash?: string; // 添加源文件哈希用于缓存标识
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -111,6 +112,12 @@ export class EnhancedFormUpdateService {
|
|
|
private downloadingImages: Map<string, Promise<string | null>> = new Map();
|
|
|
private readonly imageCacheExpiry: number = 24 * 60 * 60 * 1000; // 24小时
|
|
|
private readonly maxCacheSize: number = 50; // 最多缓存50张图片
|
|
|
+
|
|
|
+ // 本地图片处理缓存
|
|
|
+ private localImageCache: Map<string, ProcessedImageInfo> = new Map();
|
|
|
+ private processingLocalImages: Map<string, Promise<ProcessedImageInfo | null>> = new Map();
|
|
|
+ private readonly localImageCacheExpiry: number = 30 * 60 * 1000; // 30分钟
|
|
|
+ private lastLocalCacheCleanup: number = 0;
|
|
|
|
|
|
// 性能统计
|
|
|
private updateStats: UpdateStats = {
|
|
|
@@ -450,6 +457,66 @@ export class EnhancedFormUpdateService {
|
|
|
private async processLocalImageFile(fileUri: string): Promise<ProcessedImageInfo | null> {
|
|
|
try {
|
|
|
hilog.info(0x0000, TAG, `📷 Processing local image file: ${fileUri}`);
|
|
|
+
|
|
|
+ // 生成缓存键(基于URI和时间戳)
|
|
|
+ const cacheKey = this.generateLocalImageCacheKey(fileUri);
|
|
|
+
|
|
|
+ // 检查本地图片缓存
|
|
|
+ const cachedInfo = this.localImageCache.get(cacheKey);
|
|
|
+ if (cachedInfo) {
|
|
|
+ // 验证缓存的文件是否还存在
|
|
|
+ const tempFilePath = `${this.appContext!.getApplicationContext().tempDir}/${cachedInfo.fileName}`;
|
|
|
+ if (fileIo.accessSync(tempFilePath)) {
|
|
|
+ hilog.info(0x0000, TAG, `📷 Using cached local image: ${cachedInfo.fileName}`);
|
|
|
+ return cachedInfo;
|
|
|
+ } else {
|
|
|
+ // 缓存文件不存在,移除缓存项
|
|
|
+ this.localImageCache.delete(cacheKey);
|
|
|
+ hilog.warn(0x0000, TAG, `📷 Cached file not found, will reprocess: ${cachedInfo.fileName}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查是否正在处理中
|
|
|
+ if (this.processingLocalImages.has(cacheKey)) {
|
|
|
+ hilog.info(0x0000, TAG, `📷 Image already being processed, waiting: ${fileUri}`);
|
|
|
+ return await this.processingLocalImages.get(cacheKey)!;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建处理Promise
|
|
|
+ const processingPromise = this.performLocalImageProcessing(fileUri, cacheKey);
|
|
|
+ this.processingLocalImages.set(cacheKey, processingPromise);
|
|
|
+
|
|
|
+ try {
|
|
|
+ const result = await processingPromise;
|
|
|
+ return result;
|
|
|
+ } finally {
|
|
|
+ this.processingLocalImages.delete(cacheKey);
|
|
|
+ }
|
|
|
+
|
|
|
+ } catch (error) {
|
|
|
+ hilog.error(0x0000, TAG, `❌ Failed to process local image: ${error}`);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 生成本地图片缓存键
|
|
|
+ */
|
|
|
+ private generateLocalImageCacheKey(fileUri: string): string {
|
|
|
+ // 从URI中提取文件名或路径的关键部分作为缓存键
|
|
|
+ const pathParts = fileUri.split('/');
|
|
|
+ const fileName = pathParts[pathParts.length - 1];
|
|
|
+ // 使用文件名作为缓存键(因为日志显示相同的文件被重复处理)
|
|
|
+ return `local_${fileName}`;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 执行本地图片处理
|
|
|
+ */
|
|
|
+ private async performLocalImageProcessing(fileUri: string, cacheKey: string): Promise<ProcessedImageInfo | null> {
|
|
|
+ try {
|
|
|
+ // 清理过期缓存
|
|
|
+ this.cleanupLocalImageCache();
|
|
|
|
|
|
// 转换 file:// URI 为实际文件路径
|
|
|
// file://com.xgplayer.ttmusic.hm/data/storage/el2/base/haps/entry/files/xxx.jpg
|
|
|
@@ -523,9 +590,14 @@ export class EnhancedFormUpdateService {
|
|
|
const result: ProcessedImageInfo = {
|
|
|
fileName,
|
|
|
memoryUri,
|
|
|
- fd
|
|
|
+ fd,
|
|
|
+ sourceHash: cacheKey
|
|
|
};
|
|
|
|
|
|
+ // 添加到缓存
|
|
|
+ this.localImageCache.set(cacheKey, result);
|
|
|
+ hilog.info(0x0000, TAG, `📷 Added to local image cache: ${cacheKey}`);
|
|
|
+
|
|
|
return result;
|
|
|
|
|
|
} catch (error) {
|
|
|
@@ -541,6 +613,72 @@ export class EnhancedFormUpdateService {
|
|
|
return url.startsWith('file://');
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 清理本地图片缓存
|
|
|
+ */
|
|
|
+ private cleanupLocalImageCache(): void {
|
|
|
+ const now = Date.now();
|
|
|
+
|
|
|
+ // 每5分钟清理一次
|
|
|
+ if (now - this.lastLocalCacheCleanup < 5 * 60 * 1000) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ this.lastLocalCacheCleanup = now;
|
|
|
+
|
|
|
+ // 清理过期的缓存项
|
|
|
+ const keysToDelete: string[] = [];
|
|
|
+ const tempDir = this.appContext!.getApplicationContext().tempDir;
|
|
|
+
|
|
|
+ this.localImageCache.forEach((info, key) => {
|
|
|
+ const tempFilePath = `${tempDir}/${info.fileName}`;
|
|
|
+
|
|
|
+ // 检查文件是否存在
|
|
|
+ if (!fileIo.accessSync(tempFilePath)) {
|
|
|
+ keysToDelete.push(key);
|
|
|
+ hilog.info(0x0000, TAG, `📷 Removing cache entry for missing file: ${info.fileName}`);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 如果缓存过大,删除最旧的项
|
|
|
+ if (this.localImageCache.size > 20) {
|
|
|
+ const sortedEntries = Array.from(this.localImageCache.entries())
|
|
|
+ .sort((a, b) => {
|
|
|
+ // 根据文件名中的时间戳排序
|
|
|
+ const timeA = parseInt(a[1].fileName.split('_')[2] || '0');
|
|
|
+ const timeB = parseInt(b[1].fileName.split('_')[2] || '0');
|
|
|
+ return timeA - timeB;
|
|
|
+ });
|
|
|
+
|
|
|
+ const entriesToDelete = sortedEntries.slice(0, this.localImageCache.size - 15);
|
|
|
+ entriesToDelete.forEach((entry) => {
|
|
|
+ const key = entry[0];
|
|
|
+ const info = entry[1];
|
|
|
+ keysToDelete.push(key);
|
|
|
+ // 尝试删除临时文件
|
|
|
+ try {
|
|
|
+ const filePath = `${tempDir}/${info.fileName}`;
|
|
|
+ if (fileIo.accessSync(filePath)) {
|
|
|
+ fileIo.closeSync(info.fd);
|
|
|
+ fileIo.unlinkSync(filePath);
|
|
|
+ hilog.info(0x0000, TAG, `📷 Deleted old cached file: ${info.fileName}`);
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ hilog.warn(0x0000, TAG, `📷 Failed to delete cached file: ${info.fileName}`);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ // 删除缓存项
|
|
|
+ keysToDelete.forEach(key => {
|
|
|
+ this.localImageCache.delete(key);
|
|
|
+ });
|
|
|
+
|
|
|
+ if (keysToDelete.length > 0) {
|
|
|
+ hilog.info(0x0000, TAG, `📷 Cleaned up ${keysToDelete.length} local image cache entries`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 获取文件扩展名
|
|
|
*/
|
|
|
@@ -561,12 +699,21 @@ export class EnhancedFormUpdateService {
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
- // 如果已经是 memory:// 格式(本地图片已处理),直接返回
|
|
|
+ // 如果已经是 memory:// 格式(本地图片已处理),验证缓存是否有效
|
|
|
if (adaptedData.coverImage.startsWith('memory://')) {
|
|
|
- hilog.info(0x0000, TAG, `📷 Image already processed as memory URI: ${adaptedData.coverImage}`);
|
|
|
const fileName = adaptedData.coverImage.replace('memory://', '');
|
|
|
- adaptedData.imgName = fileName;
|
|
|
- return;
|
|
|
+ const tempFilePath = `${this.appContext!.getApplicationContext().tempDir}/${fileName}`;
|
|
|
+
|
|
|
+ // 验证文件是否存在
|
|
|
+ if (fileIo.accessSync(tempFilePath)) {
|
|
|
+ hilog.info(0x0000, TAG, `📷 Using existing memory URI: ${adaptedData.coverImage}`);
|
|
|
+ adaptedData.imgName = fileName;
|
|
|
+ return;
|
|
|
+ } else {
|
|
|
+ hilog.warn(0x0000, TAG, `📷 Memory URI file not found, will reprocess: ${fileName}`);
|
|
|
+ // 清除无效的memory URI,继续处理
|
|
|
+ adaptedData.coverImage = '';
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// 处理网络图片
|
|
|
@@ -948,6 +1095,32 @@ export class EnhancedFormUpdateService {
|
|
|
};
|
|
|
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 清理所有缓存
|
|
|
+ */
|
|
|
+ public clearAllCaches(): void {
|
|
|
+ // 清理本地图片缓存
|
|
|
+ this.localImageCache.forEach((info) => {
|
|
|
+ try {
|
|
|
+ const tempFilePath = `${this.appContext!.getApplicationContext().tempDir}/${info.fileName}`;
|
|
|
+ if (fileIo.accessSync(tempFilePath)) {
|
|
|
+ fileIo.closeSync(info.fd);
|
|
|
+ fileIo.unlinkSync(tempFilePath);
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ hilog.warn(0x0000, TAG, `Failed to clean cached file: ${info.fileName}`);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ this.localImageCache.clear();
|
|
|
+ this.processingLocalImages.clear();
|
|
|
+
|
|
|
+ // 清理网络图片缓存
|
|
|
+ this.imageCache.clear();
|
|
|
+ this.downloadingImages.clear();
|
|
|
+
|
|
|
+ hilog.info(0x0000, TAG, '🧹 All caches cleared');
|
|
|
+ }
|
|
|
|
|
|
/**
|
|
|
* 强制刷新所有卡片(忽略防抖)
|