修复暂停下载后不能恢复和更改时间无效的问题
This commit is contained in:
@@ -33,6 +33,10 @@ class DownloadExecutor {
|
||||
// 检查是否应该暂停
|
||||
if (this.shouldPause(task.id)) {
|
||||
logger.info('任务已暂停,停止下载:', task.id);
|
||||
// 确保任务状态为暂停
|
||||
task.status = 'paused';
|
||||
await this.taskManager.saveTasks();
|
||||
this.progressManager.notifyProgressUpdate(task.id, task);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -75,10 +79,13 @@ class DownloadExecutor {
|
||||
// 验证文件完整性
|
||||
const integrity = await this.fileManager.checkFileIntegrity(filePath);
|
||||
if (integrity.valid) {
|
||||
task.completed_files++;
|
||||
task.progress = Math.round((task.completed_files / task.total_files) * 100);
|
||||
await this.taskManager.saveTasks();
|
||||
this.progressManager.notifyProgressUpdate(task.id, task);
|
||||
// 只有在非恢复模式下才增加计数,避免重复计算
|
||||
if (!task.isResuming) {
|
||||
task.completed_files++;
|
||||
task.progress = Math.round((task.completed_files / task.total_files) * 100);
|
||||
await this.taskManager.saveTasks();
|
||||
this.progressManager.notifyProgressUpdate(task.id, task);
|
||||
}
|
||||
results.push({ success: true, file: fileName, skipped: true });
|
||||
continue;
|
||||
} else {
|
||||
@@ -115,6 +122,12 @@ class DownloadExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
// 检查任务是否被暂停,如果是则不要更新最终状态
|
||||
if (task.status === 'paused') {
|
||||
logger.info('任务已暂停,跳过最终状态更新:', task.id);
|
||||
return;
|
||||
}
|
||||
|
||||
// 保存作品信息
|
||||
const infoPath = path.join(artworkDir, 'artwork_info.json');
|
||||
await fs.writeJson(infoPath, artwork, { spaces: 2 });
|
||||
@@ -527,32 +540,123 @@ class DownloadExecutor {
|
||||
async resumeTask(taskId) {
|
||||
const task = this.taskManager.getTask(taskId);
|
||||
if (!task) {
|
||||
logger.error('恢复任务失败:任务不存在', { taskId });
|
||||
throw new Error('任务不存在');
|
||||
}
|
||||
|
||||
// logger.info('下载执行器检查任务状态', {
|
||||
// taskId,
|
||||
// currentStatus: task.status,
|
||||
// type: task.type
|
||||
// });
|
||||
|
||||
if (task.status !== 'paused') {
|
||||
logger.error('恢复任务失败:任务状态不是暂停状态', {
|
||||
taskId,
|
||||
currentStatus: task.status
|
||||
});
|
||||
throw new Error('任务状态不是暂停状态');
|
||||
}
|
||||
|
||||
// 根据任务类型重新开始下载
|
||||
if (task.type === 'artwork') {
|
||||
// logger.info('开始恢复单个作品下载任务', { taskId, artwork_id: task.artwork_id });
|
||||
|
||||
// 重新获取作品信息和图片URL
|
||||
const artworkResult = await this.downloadService.artworkService.getArtwork(task.artwork_id);
|
||||
const artworkResult = await this.downloadService.artworkService.getArtworkDetail(task.artwork_id);
|
||||
if (!artworkResult.success) {
|
||||
logger.error('获取作品信息失败', { taskId, error: artworkResult.error });
|
||||
throw new Error(`获取作品信息失败: ${artworkResult.error}`);
|
||||
}
|
||||
|
||||
const imagesResult = await this.downloadService.artworkService.getArtworkImages(task.artwork_id, 'original');
|
||||
let imagesResult = await this.downloadService.artworkService.getArtworkImages(task.artwork_id, 'original');
|
||||
if (!imagesResult.success) {
|
||||
logger.error('获取图片URL失败', { taskId, error: imagesResult.error });
|
||||
throw new Error(`获取图片URL失败: ${imagesResult.error}`);
|
||||
}
|
||||
|
||||
const artwork = artworkResult.data;
|
||||
const images = imagesResult.data.images;
|
||||
const artworkDir = await this.fileManager.getArtworkDirectory(artwork);
|
||||
let images = imagesResult.data.images;
|
||||
|
||||
// 创建作品目录(使用与DownloadService相同的逻辑)
|
||||
const artistName = this.fileManager.createSafeDirectoryName(artwork.user.name || 'Unknown Artist');
|
||||
const artworkTitle = this.fileManager.createSafeDirectoryName(artwork.title || 'Untitled');
|
||||
const downloadPath = await this.fileManager.getDownloadPath();
|
||||
const artistDir = path.join(downloadPath, artistName);
|
||||
const artworkDirName = `${task.artwork_id}_${artworkTitle}`;
|
||||
const artworkDir = path.join(artistDir, artworkDirName);
|
||||
|
||||
// 重新开始下载
|
||||
this.executeArtworkDownload(task, images, 'original', artworkDir, artwork);
|
||||
// logger.info('准备恢复下载,重置任务状态', {
|
||||
// taskId,
|
||||
// originalTotalFiles: task.total_files,
|
||||
// newImageCount: images.length,
|
||||
// artworkDir
|
||||
// });
|
||||
|
||||
// 如果新获取的图片数量与原始数量不同,记录警告但使用原始数量
|
||||
if (images.length !== task.total_files) {
|
||||
logger.warn('恢复时图片数量发生变化', {
|
||||
taskId,
|
||||
originalTotalFiles: task.total_files,
|
||||
newImageCount: images.length
|
||||
});
|
||||
// 使用原始数量,避免任务状态混乱
|
||||
images = images.slice(0, task.total_files);
|
||||
}
|
||||
|
||||
// 检查哪些文件已经完成下载
|
||||
const completedFiles = [];
|
||||
const incompleteFiles = [];
|
||||
|
||||
for (let index = 0; index < images.length; index++) {
|
||||
const imageObj = images[index];
|
||||
let imageUrl = imageObj.original || imageObj.large || imageObj.medium;
|
||||
const fileName = `image_${index + 1}.${this.getFileExtension(imageUrl)}`;
|
||||
const filePath = path.join(artworkDir, fileName);
|
||||
|
||||
// 检查文件是否存在且完整
|
||||
if (await this.fileManager.fileExists(filePath)) {
|
||||
const integrity = await this.fileManager.checkFileIntegrity(filePath);
|
||||
if (integrity.valid) {
|
||||
completedFiles.push({ index, fileName, filePath });
|
||||
} else {
|
||||
incompleteFiles.push({ index, fileName, filePath });
|
||||
}
|
||||
} else {
|
||||
incompleteFiles.push({ index, fileName, filePath });
|
||||
}
|
||||
}
|
||||
|
||||
// logger.info('文件检查完成', {
|
||||
// taskId,
|
||||
// completedCount: completedFiles.length,
|
||||
// incompleteCount: incompleteFiles.length
|
||||
// });
|
||||
|
||||
// 只删除未完成的文件
|
||||
for (const fileInfo of incompleteFiles) {
|
||||
try {
|
||||
await this.fileManager.safeDeleteFile(fileInfo.filePath);
|
||||
logger.debug(`删除未完成文件: ${fileInfo.fileName}`);
|
||||
} catch (error) {
|
||||
// 忽略删除错误,文件可能不存在
|
||||
logger.debug(`删除文件失败(可能不存在): ${fileInfo.filePath}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 重置任务状态,但保留已完成的文件计数
|
||||
task.completed_files = completedFiles.length;
|
||||
task.failed_files = 0;
|
||||
task.progress = Math.round((task.completed_files / task.total_files) * 100);
|
||||
task.status = 'downloading';
|
||||
// 添加恢复标志,避免重复计算已完成的文件
|
||||
task.isResuming = true;
|
||||
await this.taskManager.saveTasks();
|
||||
|
||||
// logger.info('开始执行作品下载', { taskId });
|
||||
|
||||
// 重新开始下载 - 等待异步执行开始
|
||||
await this.executeArtworkDownload(task, images, 'original', artworkDir, artwork);
|
||||
} else if (task.type === 'batch' || task.type === 'artist') {
|
||||
// 批量下载和作者下载的恢复逻辑
|
||||
// 这里需要根据具体实现来恢复
|
||||
@@ -560,6 +664,7 @@ class DownloadExecutor {
|
||||
// TODO: 实现批量下载的恢复逻辑
|
||||
}
|
||||
|
||||
logger.info('任务恢复执行完成', { taskId });
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
|
||||
@@ -224,32 +224,67 @@ class DownloadService {
|
||||
return { success: false, error: '任务不存在' };
|
||||
}
|
||||
|
||||
// 只允许暂停正在下载的任务
|
||||
if (task.status !== 'downloading') {
|
||||
return { success: false, error: '只能暂停正在下载的任务' };
|
||||
}
|
||||
|
||||
await this.taskManager.updateTask(taskId, { status: 'paused' });
|
||||
this.progressManager.notifyProgressUpdate(taskId, task);
|
||||
return { success: true };
|
||||
|
||||
// 获取更新后的任务
|
||||
const updatedTask = this.taskManager.getTask(taskId);
|
||||
this.progressManager.notifyProgressUpdate(taskId, updatedTask);
|
||||
|
||||
return { success: true, data: updatedTask };
|
||||
}
|
||||
|
||||
async resumeTask(taskId) {
|
||||
const task = this.taskManager.getTask(taskId);
|
||||
if (!task) {
|
||||
logger.error('恢复任务失败:任务不存在', { taskId });
|
||||
return { success: false, error: '任务不存在' };
|
||||
}
|
||||
|
||||
// logger.info('尝试恢复任务', {
|
||||
// taskId,
|
||||
// currentStatus: task.status,
|
||||
// type: task.type,
|
||||
// artwork_id: task.artwork_id
|
||||
// });
|
||||
|
||||
// 只允许恢复暂停的任务
|
||||
if (task.status !== 'paused') {
|
||||
return { success: false, error: '任务状态不是暂停状态' };
|
||||
logger.warn('恢复任务失败:任务状态不是暂停状态', {
|
||||
taskId,
|
||||
currentStatus: task.status
|
||||
});
|
||||
return { success: false, error: '只能恢复暂停的任务' };
|
||||
}
|
||||
|
||||
// 更新任务状态为下载中
|
||||
await this.taskManager.updateTask(taskId, { status: 'downloading' });
|
||||
|
||||
// 通知进度更新
|
||||
const updatedTask = this.taskManager.getTask(taskId);
|
||||
this.progressManager.notifyProgressUpdate(taskId, updatedTask);
|
||||
|
||||
// 重新开始下载执行
|
||||
this.downloadExecutor.resumeTask(taskId);
|
||||
try {
|
||||
logger.info('开始恢复任务执行', { taskId });
|
||||
await this.downloadExecutor.resumeTask(taskId);
|
||||
|
||||
// 获取更新后的任务状态
|
||||
const updatedTask = this.taskManager.getTask(taskId);
|
||||
this.progressManager.notifyProgressUpdate(taskId, updatedTask);
|
||||
|
||||
logger.info('任务恢复成功', {
|
||||
taskId,
|
||||
newStatus: updatedTask.status
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('恢复任务执行失败', {
|
||||
taskId,
|
||||
error: error.message,
|
||||
stack: error.stack
|
||||
});
|
||||
// 如果恢复失败,保持暂停状态
|
||||
return { success: false, error: `恢复任务失败: ${error.message}` };
|
||||
}
|
||||
|
||||
return { success: true, data: updatedTask };
|
||||
return { success: true, data: this.taskManager.getTask(taskId) };
|
||||
}
|
||||
|
||||
// 代理方法 - 历史记录管理
|
||||
|
||||
+192
-21
@@ -20,13 +20,16 @@ class ImageCacheService {
|
||||
if (isPkg) {
|
||||
// 在打包环境中,使用可执行文件所在目录
|
||||
this.cacheDir = path.join(process.cwd(), 'data', 'image-cache');
|
||||
this.indexPath = path.join(process.cwd(), 'data', 'image-cache-index.json');
|
||||
} else {
|
||||
// 在开发环境中,使用项目根目录的data文件夹
|
||||
this.cacheDir = path.join(__dirname, '..', '..', 'data', 'image-cache');
|
||||
this.indexPath = path.join(__dirname, '..', '..', 'data', 'image-cache-index.json');
|
||||
}
|
||||
|
||||
// 确保路径是绝对路径
|
||||
this.cacheDir = path.resolve(this.cacheDir);
|
||||
this.indexPath = path.resolve(this.indexPath);
|
||||
|
||||
// 创建配置管理器
|
||||
this.configManager = new CacheConfigManager();
|
||||
@@ -46,6 +49,9 @@ class ImageCacheService {
|
||||
allowedExtensions: ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp'],
|
||||
};
|
||||
|
||||
// 缓存索引
|
||||
this.cacheIndex = new Map();
|
||||
|
||||
// 初始化配置
|
||||
this.initializeConfig();
|
||||
}
|
||||
@@ -62,6 +68,12 @@ class ImageCacheService {
|
||||
// 确保缓存目录存在
|
||||
await this.ensureCacheDir();
|
||||
|
||||
// 加载缓存索引
|
||||
await this.loadCacheIndex();
|
||||
|
||||
// 验证并同步缓存索引
|
||||
await this.validateAndSyncIndex();
|
||||
|
||||
// 启动定期清理任务
|
||||
this.startCleanupTask();
|
||||
|
||||
@@ -170,9 +182,21 @@ class ImageCacheService {
|
||||
*/
|
||||
async saveToCache(url, data) {
|
||||
try {
|
||||
const cacheKey = this.generateCacheKey(url);
|
||||
const cachePath = this.getCacheFilePath(url);
|
||||
const filename = path.basename(cachePath);
|
||||
|
||||
await fs.writeFile(cachePath, data);
|
||||
|
||||
// 获取文件信息并添加到索引
|
||||
const stats = await fs.stat(cachePath);
|
||||
this.addToIndex(cacheKey, filename, stats.size, stats.mtime.getTime());
|
||||
|
||||
// 异步保存索引
|
||||
this.saveCacheIndex().catch(error => {
|
||||
logger.error('异步保存缓存索引失败:', error);
|
||||
});
|
||||
|
||||
// 检查缓存大小,如果超过限制则清理
|
||||
await this.checkCacheSize();
|
||||
} catch (error) {
|
||||
@@ -256,25 +280,27 @@ class ImageCacheService {
|
||||
*/
|
||||
async checkCacheSize() {
|
||||
try {
|
||||
const files = await fs.readdir(this.cacheDir);
|
||||
let totalSize = 0;
|
||||
const fileStats = [];
|
||||
|
||||
// 计算总大小和收集文件信息
|
||||
for (const file of files) {
|
||||
const filePath = path.join(this.cacheDir, file);
|
||||
// 使用索引计算总大小和收集文件信息
|
||||
for (const [cacheKey, fileInfo] of this.cacheIndex.entries()) {
|
||||
const filePath = path.join(this.cacheDir, fileInfo.filename);
|
||||
try {
|
||||
// 验证文件是否实际存在
|
||||
const stats = await fs.stat(filePath);
|
||||
totalSize += stats.size;
|
||||
fileStats.push({
|
||||
path: filePath,
|
||||
size: stats.size,
|
||||
mtime: stats.mtime
|
||||
mtime: new Date(fileInfo.mtime),
|
||||
cacheKey: cacheKey
|
||||
});
|
||||
} catch (error) {
|
||||
// 如果文件不存在,记录日志但继续处理其他文件
|
||||
// 如果文件不存在,从索引中移除
|
||||
if (error.code === 'ENOENT') {
|
||||
logger.warn(`缓存文件不存在,跳过: ${filePath}`);
|
||||
logger.warn(`缓存文件不存在,从索引中移除: ${filePath}`);
|
||||
this.removeFromIndex(cacheKey);
|
||||
} else {
|
||||
logger.error(`检查缓存文件失败: ${filePath}`, error);
|
||||
}
|
||||
@@ -293,6 +319,9 @@ class ImageCacheService {
|
||||
await fs.unlink(file.path);
|
||||
totalSize -= file.size;
|
||||
|
||||
// 从索引中移除
|
||||
this.removeFromIndex(file.cacheKey);
|
||||
|
||||
if (totalSize <= this.config.maxSize * 0.8) { // 清理到80%
|
||||
break;
|
||||
}
|
||||
@@ -300,12 +329,17 @@ class ImageCacheService {
|
||||
// 如果删除文件失败,记录日志但继续处理其他文件
|
||||
if (error.code === 'ENOENT') {
|
||||
logger.warn(`删除缓存文件时文件不存在: ${file.path}`);
|
||||
// 从索引中移除
|
||||
this.removeFromIndex(file.cacheKey);
|
||||
} else {
|
||||
logger.error(`删除缓存文件失败: ${file.path}`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 保存更新后的索引
|
||||
await this.saveCacheIndex();
|
||||
|
||||
logger.info(`缓存清理完成,当前大小: ${totalSize}`);
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -319,31 +353,35 @@ class ImageCacheService {
|
||||
*/
|
||||
async cleanupExpiredCache() {
|
||||
try {
|
||||
const files = await fs.readdir(this.cacheDir);
|
||||
let cleanedCount = 0;
|
||||
const now = Date.now();
|
||||
|
||||
for (const file of files) {
|
||||
const filePath = path.join(this.cacheDir, file);
|
||||
// 使用索引检查过期文件
|
||||
for (const [cacheKey, fileInfo] of this.cacheIndex.entries()) {
|
||||
const filePath = path.join(this.cacheDir, fileInfo.filename);
|
||||
try {
|
||||
const stats = await fs.stat(filePath);
|
||||
|
||||
const age = Date.now() - stats.mtime.getTime();
|
||||
const age = now - stats.mtime.getTime();
|
||||
if (age > this.config.maxAge) {
|
||||
try {
|
||||
await fs.unlink(filePath);
|
||||
this.removeFromIndex(cacheKey);
|
||||
cleanedCount++;
|
||||
} catch (deleteError) {
|
||||
if (deleteError.code === 'ENOENT') {
|
||||
logger.warn(`删除过期缓存文件时文件不存在: ${filePath}`);
|
||||
this.removeFromIndex(cacheKey);
|
||||
} else {
|
||||
logger.error(`删除过期缓存文件失败: ${filePath}`, deleteError);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// 如果文件不存在,记录日志但继续处理其他文件
|
||||
// 如果文件不存在,从索引中移除
|
||||
if (error.code === 'ENOENT') {
|
||||
logger.warn(`过期缓存文件不存在,跳过: ${filePath}`);
|
||||
logger.warn(`过期缓存文件不存在,从索引中移除: ${filePath}`);
|
||||
this.removeFromIndex(cacheKey);
|
||||
} else {
|
||||
logger.error(`检查过期缓存文件失败: ${filePath}`, error);
|
||||
}
|
||||
@@ -351,6 +389,8 @@ class ImageCacheService {
|
||||
}
|
||||
|
||||
if (cleanedCount > 0) {
|
||||
// 保存更新后的索引
|
||||
await this.saveCacheIndex();
|
||||
logger.info(`清理了 ${cleanedCount} 个过期缓存文件`);
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -375,12 +415,12 @@ class ImageCacheService {
|
||||
*/
|
||||
async clearAllCache() {
|
||||
try {
|
||||
const files = await fs.readdir(this.cacheDir);
|
||||
let deletedCount = 0;
|
||||
let errorCount = 0;
|
||||
|
||||
for (const file of files) {
|
||||
const filePath = path.join(this.cacheDir, file);
|
||||
// 使用索引清理所有文件
|
||||
for (const [cacheKey, fileInfo] of this.cacheIndex.entries()) {
|
||||
const filePath = path.join(this.cacheDir, fileInfo.filename);
|
||||
try {
|
||||
await fs.unlink(filePath);
|
||||
deletedCount++;
|
||||
@@ -394,6 +434,10 @@ class ImageCacheService {
|
||||
}
|
||||
}
|
||||
|
||||
// 清空索引
|
||||
this.cacheIndex.clear();
|
||||
await this.saveCacheIndex();
|
||||
|
||||
if (errorCount === 0) {
|
||||
logger.info(`所有缓存已清理,共删除 ${deletedCount} 个文件`);
|
||||
} else {
|
||||
@@ -411,13 +455,14 @@ class ImageCacheService {
|
||||
*/
|
||||
async getCacheStats() {
|
||||
try {
|
||||
const files = await fs.readdir(this.cacheDir);
|
||||
let totalSize = 0;
|
||||
let fileCount = 0;
|
||||
let errorCount = 0;
|
||||
let indexSize = this.cacheIndex.size;
|
||||
|
||||
for (const file of files) {
|
||||
const filePath = path.join(this.cacheDir, file);
|
||||
// 使用索引获取统计信息
|
||||
for (const [cacheKey, fileInfo] of this.cacheIndex.entries()) {
|
||||
const filePath = path.join(this.cacheDir, fileInfo.filename);
|
||||
try {
|
||||
const stats = await fs.stat(filePath);
|
||||
totalSize += stats.size;
|
||||
@@ -425,6 +470,8 @@ class ImageCacheService {
|
||||
} catch (error) {
|
||||
if (error.code === 'ENOENT') {
|
||||
logger.warn(`统计缓存时文件不存在: ${filePath}`);
|
||||
// 从索引中移除不存在的文件
|
||||
this.removeFromIndex(cacheKey);
|
||||
} else {
|
||||
logger.error(`获取缓存文件统计失败: ${filePath}`, error);
|
||||
}
|
||||
@@ -432,6 +479,11 @@ class ImageCacheService {
|
||||
}
|
||||
}
|
||||
|
||||
// 如果有文件被移除,保存索引
|
||||
if (errorCount > 0) {
|
||||
await this.saveCacheIndex();
|
||||
}
|
||||
|
||||
return {
|
||||
fileCount,
|
||||
totalSize,
|
||||
@@ -439,7 +491,8 @@ class ImageCacheService {
|
||||
maxAge: this.config.maxAge,
|
||||
enabled: this.config.enabled,
|
||||
config: this.config,
|
||||
errorCount
|
||||
errorCount,
|
||||
indexSize
|
||||
};
|
||||
} catch (error) {
|
||||
logger.error('获取缓存统计失败:', error);
|
||||
@@ -450,7 +503,8 @@ class ImageCacheService {
|
||||
maxAge: this.config.maxAge,
|
||||
enabled: this.config.enabled,
|
||||
config: this.config,
|
||||
errorCount: 0
|
||||
errorCount: 0,
|
||||
indexSize: this.cacheIndex.size
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -483,6 +537,123 @@ class ImageCacheService {
|
||||
this.config = { ...this.config, ...defaultConfig };
|
||||
return defaultConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载缓存索引
|
||||
*/
|
||||
async loadCacheIndex() {
|
||||
try {
|
||||
if (await fs.access(this.indexPath).then(() => true).catch(() => false)) {
|
||||
const indexData = await fs.readFile(this.indexPath, 'utf8');
|
||||
const index = JSON.parse(indexData);
|
||||
this.cacheIndex = new Map(Object.entries(index));
|
||||
logger.info(`已加载缓存索引,包含 ${this.cacheIndex.size} 个文件记录`);
|
||||
} else {
|
||||
logger.info('缓存索引文件不存在,将创建新的索引');
|
||||
this.cacheIndex = new Map();
|
||||
}
|
||||
} catch (error) {
|
||||
logger.warn('加载缓存索引失败,将创建新的索引:', error.message);
|
||||
this.cacheIndex = new Map();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存缓存索引
|
||||
*/
|
||||
async saveCacheIndex() {
|
||||
try {
|
||||
const indexData = Object.fromEntries(this.cacheIndex);
|
||||
await fs.writeFile(this.indexPath, JSON.stringify(indexData, null, 2));
|
||||
} catch (error) {
|
||||
logger.error('保存缓存索引失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证并同步缓存索引
|
||||
*/
|
||||
async validateAndSyncIndex() {
|
||||
try {
|
||||
const files = await fs.readdir(this.cacheDir);
|
||||
const fileSet = new Set(files);
|
||||
let removedCount = 0;
|
||||
let addedCount = 0;
|
||||
|
||||
// 检查索引中的文件是否实际存在
|
||||
for (const [cacheKey, fileInfo] of this.cacheIndex.entries()) {
|
||||
if (!fileSet.has(fileInfo.filename)) {
|
||||
this.cacheIndex.delete(cacheKey);
|
||||
removedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查实际文件是否在索引中
|
||||
for (const filename of files) {
|
||||
const filePath = path.join(this.cacheDir, filename);
|
||||
try {
|
||||
const stats = await fs.stat(filePath);
|
||||
const cacheKey = this.findCacheKeyByFilename(filename);
|
||||
|
||||
if (!cacheKey) {
|
||||
// 文件存在但不在索引中,添加到索引
|
||||
this.cacheIndex.set(filename, {
|
||||
filename: filename,
|
||||
size: stats.size,
|
||||
mtime: stats.mtime.getTime(),
|
||||
added: Date.now()
|
||||
});
|
||||
addedCount++;
|
||||
}
|
||||
} catch (error) {
|
||||
// 文件不存在,从索引中移除
|
||||
const cacheKey = this.findCacheKeyByFilename(filename);
|
||||
if (cacheKey) {
|
||||
this.cacheIndex.delete(cacheKey);
|
||||
removedCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (removedCount > 0 || addedCount > 0) {
|
||||
logger.info(`缓存索引同步完成: 移除 ${removedCount} 个无效记录,添加 ${addedCount} 个新记录`);
|
||||
await this.saveCacheIndex();
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('验证缓存索引失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据文件名查找缓存键
|
||||
*/
|
||||
findCacheKeyByFilename(filename) {
|
||||
for (const [cacheKey, fileInfo] of this.cacheIndex.entries()) {
|
||||
if (fileInfo.filename === filename) {
|
||||
return cacheKey;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加文件到缓存索引
|
||||
*/
|
||||
addToIndex(cacheKey, filename, size, mtime) {
|
||||
this.cacheIndex.set(cacheKey, {
|
||||
filename: filename,
|
||||
size: size,
|
||||
mtime: mtime,
|
||||
added: Date.now()
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 从缓存索引中移除文件
|
||||
*/
|
||||
removeFromIndex(cacheKey) {
|
||||
this.cacheIndex.delete(cacheKey);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ImageCacheService;
|
||||
Reference in New Issue
Block a user