diff --git a/backend/services/download.js b/backend/services/download.js
index 544a35a..2b35114 100644
--- a/backend/services/download.js
+++ b/backend/services/download.js
@@ -317,42 +317,25 @@ class DownloadService {
if (artworkMatch && artworkMatch[1] === artworkId.toString()) {
const artworkPath = path.join(artistPath, artworkEntry);
- // 检查作品信息文件
+ // 检查作品信息文件 - 这是最可靠的判断标准
const infoPath = path.join(artworkPath, 'artwork_info.json');
if (!(await this.fileManager.fileExists(infoPath))) {
- console.log(`作品 ${artworkId} 缺少信息文件`);
+ console.log(`作品 ${artworkId} 缺少信息文件,认为未下载`);
return false;
}
- // 检查图片文件
+ // 检查是否有图片文件
const files = await this.fileManager.listDirectory(artworkPath);
const imageFiles = files.filter(file => /\.(jpg|jpeg|png|gif|webp)$/i.test(file) && file !== 'artwork_info.json');
if (imageFiles.length === 0) {
- console.log(`作品 ${artworkId} 没有图片文件`);
+ console.log(`作品 ${artworkId} 有信息文件但没有图片文件,认为未下载`);
return false;
}
- // 检查每个图片文件的完整性
- let validFiles = 0;
- for (const imageFile of imageFiles) {
- const imagePath = path.join(artworkPath, imageFile);
- const integrity = await this.fileManager.checkFileIntegrity(imagePath);
- if (integrity.valid) {
- validFiles++;
- } else {
- console.log(`作品 ${artworkId} 的图片文件 ${imageFile} 不完整: ${integrity.reason}`);
- }
- }
-
- // 只有当所有图片文件都完整时,才认为作品已下载
- if (validFiles === imageFiles.length) {
- console.log(`作品 ${artworkId} 已完整下载,共 ${validFiles} 个文件`);
- return true;
- } else {
- console.log(`作品 ${artworkId} 下载不完整,${validFiles}/${imageFiles.length} 个文件有效`);
- return false;
- }
+ // 有信息文件且有图片文件,认为已下载
+ console.log(`作品 ${artworkId} 已下载,有信息文件和 ${imageFiles.length} 个图片文件`);
+ return true;
}
}
}
@@ -390,7 +373,7 @@ class DownloadService {
if (artworkMatch && artworkMatch[1] === artworkId.toString()) {
const artworkPath = path.join(artistPath, artworkEntry);
- // 检查作品信息文件
+ // 检查作品信息文件 - 这是最可靠的判断标准
const infoPath = path.join(artworkPath, 'artwork_info.json');
if (!(await this.fileManager.fileExists(infoPath))) {
console.log(`作品 ${artworkId} 缺少信息文件,清理目录`);
@@ -402,12 +385,12 @@ class DownloadService {
};
}
- // 检查图片文件
+ // 检查是否有图片文件
const files = await this.fileManager.listDirectory(artworkPath);
const imageFiles = files.filter(file => /\.(jpg|jpeg|png|gif|webp)$/i.test(file) && file !== 'artwork_info.json');
if (imageFiles.length === 0) {
- console.log(`作品 ${artworkId} 没有图片文件,清理目录`);
+ console.log(`作品 ${artworkId} 有信息文件但没有图片文件,清理目录`);
await this.fileManager.removeDirectory(artworkPath);
return {
is_downloaded: false,
@@ -416,45 +399,12 @@ class DownloadService {
};
}
- // 检查每个图片文件的完整性,清理不完整的文件
- let validFiles = 0;
- for (const imageFile of imageFiles) {
- const imagePath = path.join(artworkPath, imageFile);
- const integrity = await this.fileManager.checkFileIntegrity(imagePath);
- if (integrity.valid) {
- validFiles++;
- } else {
- console.log(`作品 ${artworkId} 的图片文件 ${imageFile} 不完整,删除: ${integrity.reason}`);
- await this.fileManager.safeDeleteFile(imagePath);
- cleanedFiles++;
- }
- }
-
- // 如果所有文件都被清理了,删除整个目录
- if (validFiles === 0) {
- console.log(`作品 ${artworkId} 所有图片文件都不完整,清理目录`);
- await this.fileManager.removeDirectory(artworkPath);
- return {
- is_downloaded: false,
- cleaned_files: cleanedFiles + 1,
- message: '所有图片文件都不完整,已清理整个目录'
- };
- }
-
- // 只有当所有图片文件都完整时,才认为作品已下载
- if (validFiles === imageFiles.length) {
- return {
- is_downloaded: true,
- cleaned_files: cleanedFiles,
- message: `作品已完整下载,共 ${validFiles} 个文件`
- };
- } else {
- return {
- is_downloaded: false,
- cleaned_files: cleanedFiles,
- message: `作品下载不完整,${validFiles}/${imageFiles.length} 个文件有效,已清理 ${cleanedFiles} 个不完整文件`
- };
- }
+ // 有信息文件且有图片文件,认为已下载
+ return {
+ is_downloaded: true,
+ cleaned_files: cleanedFiles,
+ message: `作品已下载,有信息文件和 ${imageFiles.length} 个图片文件`
+ };
}
}
}
@@ -544,6 +494,9 @@ class DownloadService {
await this.taskManager.saveTasks();
+ // 立即发送初始状态更新,让前端能立即看到进度条
+ this.progressManager.notifyProgressUpdate(task.id, task);
+
// 立即返回任务ID,异步执行下载
this.downloadExecutor.executeArtworkDownload(task, images, size, artworkDir, artwork);
@@ -554,6 +507,7 @@ class DownloadService {
artwork_id: artworkId,
artist_name: artistName,
artwork_title: artworkTitle,
+ total_files: images.length,
status: 'downloading',
message: '下载任务已创建,正在后台执行',
},
@@ -599,6 +553,9 @@ class DownloadService {
await this.taskManager.saveTasks();
+ // 立即发送初始状态更新,让前端能立即看到进度条
+ this.progressManager.notifyProgressUpdate(task.id, task);
+
// 如果没有需要下载的作品,直接返回
if (filteredIds.length === 0) {
await this.taskManager.updateTask(task.id, {
diff --git a/backend/services/file-manager.js b/backend/services/file-manager.js
index 0e18db4..23e816d 100644
--- a/backend/services/file-manager.js
+++ b/backend/services/file-manager.js
@@ -84,58 +84,11 @@ class FileManager {
}
// 检查文件是否过小(可能下载不完整)
- if (stats.size < 1024) { // 小于1KB的文件可能是损坏的
+ if (stats.size < 512) { // 小于512字节的文件可能是损坏的
return { valid: false, reason: '文件过小,可能下载不完整', size: stats.size };
}
- // 检查文件头,验证是否为有效的图片文件
- try {
- const fileHandle = await fs.open(filePath, 'r');
- const buffer = Buffer.alloc(12);
- await fileHandle.read(buffer, 0, 12, 0);
- await fileHandle.close();
-
- // 检查常见图片格式的文件头
- const isJPEG = buffer[0] === 0xFF && buffer[1] === 0xD8 && buffer[2] === 0xFF;
- const isPNG = buffer[0] === 0x89 && buffer[1] === 0x50 && buffer[2] === 0x4E && buffer[3] === 0x47;
- const isGIF = (buffer[0] === 0x47 && buffer[1] === 0x49 && buffer[2] === 0x46) ||
- (buffer[0] === 0x47 && buffer[1] === 0x49 && buffer[2] === 0x46 && buffer[3] === 0x38);
- const isWebP = buffer[0] === 0x52 && buffer[1] === 0x49 && buffer[2] === 0x46 && buffer[3] === 0x46;
-
- if (!isJPEG && !isPNG && !isGIF && !isWebP) {
- return { valid: false, reason: '文件格式无效或损坏', size: stats.size };
- }
-
- // 对于JPEG文件,检查文件尾
- if (isJPEG) {
- const endBuffer = Buffer.alloc(2);
- const endHandle = await fs.open(filePath, 'r');
- await endHandle.read(endBuffer, 0, 2, stats.size - 2);
- await endHandle.close();
-
- if (endBuffer[0] !== 0xFF || endBuffer[1] !== 0xD9) {
- return { valid: false, reason: 'JPEG文件不完整(缺少结束标记)', size: stats.size };
- }
- }
-
- // 对于PNG文件,检查文件尾
- if (isPNG) {
- const endBuffer = Buffer.alloc(8);
- const endHandle = await fs.open(filePath, 'r');
- await endHandle.read(endBuffer, 0, 8, stats.size - 8);
- await endHandle.close();
-
- const pngEnd = Buffer.from([0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82]);
- if (!endBuffer.equals(pngEnd)) {
- return { valid: false, reason: 'PNG文件不完整(缺少结束标记)', size: stats.size };
- }
- }
-
- } catch (headerError) {
- console.warn('文件头检查失败,但继续验证:', headerError.message);
- // 如果文件头检查失败,但文件大小正常,仍然认为是有效的
- }
-
+ // 文件存在且大小正常,认为有效
return { valid: true, size: stats.size };
} catch (error) {
return { valid: false, reason: '检查文件失败', error: error.message };
diff --git a/ui/src/views/ArtworkView.vue b/ui/src/views/ArtworkView.vue
index 7b6ba34..efd19c4 100644
--- a/ui/src/views/ArtworkView.vue
+++ b/ui/src/views/ArtworkView.vue
@@ -55,13 +55,6 @@
重新下载
下载
-
-
-
@@ -206,7 +199,6 @@ const imageLoaded = ref(false);
const imageError = ref(false);
const downloading = ref(false);
const isDownloaded = ref(false);
-const checkingDownloadStatus = ref(false);
// 下载任务状态
const currentTask = ref(null);
@@ -296,7 +288,6 @@ const fetchArtworkDetail = async () => {
// 检查下载状态
const checkDownloadStatus = async (artworkId: number, retryCount = 0) => {
try {
- checkingDownloadStatus.value = true;
const response = await repositoryStore.checkArtworkDownloaded(artworkId);
console.log('下载状态检查响应:', response);
@@ -324,8 +315,6 @@ const checkDownloadStatus = async (artworkId: number, retryCount = 0) => {
checkDownloadStatus(artworkId, retryCount + 1);
}, 2000 * (retryCount + 1));
}
- } finally {
- checkingDownloadStatus.value = false;
}
};
@@ -355,21 +344,24 @@ const handleDownload = async () => {
return;
}
- // 如果是新任务,开始监听进度
+ // 如果是新任务,立即创建任务状态并开始监听进度
if (response.data.task_id) {
+ // 立即创建任务状态,让进度条立即显示
currentTask.value = {
id: response.data.task_id,
type: 'artwork',
status: 'downloading',
progress: 0,
- total_files: 0,
+ total_files: response.data.total_files || 0,
completed_files: 0,
failed_files: 0,
artwork_id: artwork.value.id,
+ artist_name: response.data.artist_name,
+ artwork_title: response.data.artwork_title,
start_time: new Date().toISOString()
};
- // 开始SSE监听任务进度
+ // 立即开始SSE监听任务进度
startTaskStreaming(response.data.task_id);
}
} else {
@@ -383,50 +375,6 @@ const handleDownload = async () => {
}
};
-// 强制检查下载状态
-const handleForceCheck = async () => {
- if (!artwork.value) return;
-
- try {
- checkingDownloadStatus.value = true;
- console.log('开始强制检查下载状态...');
-
- // 调用强制检查API
- const response = await fetch(`${getApiBaseUrl()}/api/download/force-check/${artwork.value.id}`, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json'
- }
- });
-
- if (response.ok) {
- const result = await response.json();
- if (result.success) {
- console.log('强制检查结果:', result.data);
-
- // 更新下载状态
- isDownloaded.value = result.data.is_downloaded;
-
- // 显示检查结果
- if (result.data.cleaned_files > 0) {
- alert(`检查完成!${result.data.message}`);
- } else {
- alert(`检查完成!${result.data.message}`);
- }
- } else {
- throw new Error(result.error || '强制检查失败');
- }
- } else {
- throw new Error('强制检查请求失败');
- }
- } catch (err) {
- console.error('强制检查失败:', err);
- alert(`强制检查失败: ${err instanceof Error ? err.message : '未知错误'}`);
- } finally {
- checkingDownloadStatus.value = false;
- }
-};
-
// 开始SSE监听任务进度
const startTaskStreaming = (taskId: string) => {
// 清除之前的连接
@@ -448,6 +396,7 @@ const startTaskStreaming = (taskId: string) => {
total: task.total_files
});
+ // 立即更新任务状态,让进度条立即显示
currentTask.value = task;
// 如果任务完成,清理连接并检查下载状态
@@ -456,8 +405,8 @@ const startTaskStreaming = (taskId: string) => {
stopTaskStreaming();
// 延迟检查下载状态,确保文件写入完成
- // 对于大文件,可能需要更长时间
- const delay = task.total_files > 1 ? 3000 : 2000; // 多文件延迟3秒,单文件延迟2秒
+ // 减少延迟时间,提高响应速度
+ const delay = task.total_files > 1 ? 1500 : 1000; // 多文件延迟1.5秒,单文件延迟1秒
setTimeout(async () => {
// 检查当前页面是否还是同一个作品,避免页面切换后的状态更新
@@ -472,7 +421,7 @@ const startTaskStreaming = (taskId: string) => {
if (artwork.value && artwork.value.id === task.artwork_id) {
await checkDownloadStatus(artwork.value.id);
}
- }, 2000);
+ }, 1000);
}
// 清理任务状态,显示下载完成状态