前端部分无用按钮删除,文件处理优化
This commit is contained in:
@@ -4,6 +4,7 @@ const path = require('path');
|
||||
const crypto = require('crypto');
|
||||
const ConfigManager = require('../config/config-manager');
|
||||
const FileUtils = require('../utils/file-utils');
|
||||
const ErrorHandler = require('../utils/error-handler');
|
||||
|
||||
/**
|
||||
* 文件管理器 - 负责文件下载、检查和目录管理
|
||||
@@ -92,32 +93,90 @@ class FileManager {
|
||||
* 简单的文件下载方法
|
||||
*/
|
||||
async downloadFile(url, filePath) {
|
||||
const response = await axios({
|
||||
method: 'GET',
|
||||
url: url,
|
||||
responseType: 'stream',
|
||||
headers: {
|
||||
'Referer': 'https://www.pixiv.net/',
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
|
||||
},
|
||||
timeout: 60000
|
||||
});
|
||||
const maxRetries = this.downloadConfig.retryAttempts;
|
||||
let lastError = null;
|
||||
|
||||
const writer = fs.createWriteStream(filePath);
|
||||
response.data.pipe(writer);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
writer.on('finish', resolve);
|
||||
writer.on('error', async (error) => {
|
||||
// 下载失败时删除文件
|
||||
try {
|
||||
await this.safeDeleteFile(filePath);
|
||||
} catch (removeError) {
|
||||
console.warn('清理失败文件时出错:', removeError.message);
|
||||
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
||||
try {
|
||||
// 使用增强的文件工具类确保目录存在
|
||||
const dirPath = path.dirname(filePath);
|
||||
const dirCreated = await FileUtils.safeEnsureDirEnhanced(dirPath);
|
||||
|
||||
if (!dirCreated) {
|
||||
throw new Error(`无法创建目录: ${dirPath}`);
|
||||
}
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
|
||||
// 检查文件是否被占用
|
||||
if (await fs.pathExists(filePath)) {
|
||||
const fileReleased = await FileUtils.waitForFileRelease(filePath);
|
||||
if (!fileReleased) {
|
||||
throw new Error(`文件被占用,无法写入: ${filePath}`);
|
||||
}
|
||||
}
|
||||
|
||||
const response = await axios({
|
||||
method: 'GET',
|
||||
url: url,
|
||||
responseType: 'stream',
|
||||
headers: {
|
||||
'Referer': 'https://www.pixiv.net/',
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
|
||||
},
|
||||
timeout: 60000
|
||||
});
|
||||
|
||||
// 使用增强的写入流创建方法
|
||||
const writer = await FileUtils.safeCreateWriteStream(filePath);
|
||||
response.data.pipe(writer);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
writer.on('finish', () => {
|
||||
// 验证文件是否写入成功
|
||||
fs.access(filePath)
|
||||
.then(() => resolve())
|
||||
.catch(error => {
|
||||
console.error(`文件写入验证失败: ${filePath}`, error.message);
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
writer.on('error', async (error) => {
|
||||
// 下载失败时删除文件
|
||||
try {
|
||||
await this.safeDeleteFile(filePath);
|
||||
} catch (removeError) {
|
||||
console.warn('清理失败文件时出错:', removeError.message);
|
||||
}
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
lastError = error;
|
||||
|
||||
// 处理文件系统错误
|
||||
const errorResult = ErrorHandler.handleFileSystemError(error, filePath, 'download');
|
||||
|
||||
console.error(`下载文件失败 (尝试 ${attempt}/${maxRetries}): ${filePath}`, error.message);
|
||||
|
||||
// 如果不是可重试的错误,直接抛出
|
||||
if (!errorResult.retryable) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
// 如果是最后一次尝试,抛出错误
|
||||
if (attempt === maxRetries) {
|
||||
console.error(`下载文件最终失败: ${filePath}`, error.message);
|
||||
throw error;
|
||||
}
|
||||
|
||||
// 等待后重试
|
||||
const retryDelay = ErrorHandler.getRetryDelay(error, attempt);
|
||||
console.log(`等待 ${retryDelay}ms 后重试下载: ${filePath}`);
|
||||
await new Promise(resolve => setTimeout(resolve, retryDelay));
|
||||
}
|
||||
}
|
||||
|
||||
// 如果所有重试都失败了
|
||||
throw lastError;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -157,7 +216,19 @@ class FileManager {
|
||||
if (!name) return 'Untitled';
|
||||
|
||||
// 移除或替换Windows文件系统不允许的字符
|
||||
let safeName = name.replace(/[<>:"/\\|?*]/g, '_');
|
||||
let safeName = name
|
||||
// 替换Windows文件系统不允许的字符
|
||||
.replace(/[<>:"/\\|?*]/g, '_')
|
||||
// 替换波浪号和其他可能导致问题的字符
|
||||
.replace(/[~`!@#$%^&*()+=\[\]{};',]/g, '_')
|
||||
// 替换控制字符
|
||||
.replace(/[\x00-\x1f\x7f]/g, '_')
|
||||
// 替换Unicode控制字符
|
||||
.replace(/[\u0000-\u001F\u007F-\u009F]/g, '_')
|
||||
// 替换零宽字符
|
||||
.replace(/[\u200B-\u200D\uFEFF]/g, '_')
|
||||
// 替换其他可能导致问题的Unicode字符
|
||||
.replace(/[\uFFFD]/g, '_');
|
||||
|
||||
// 移除前后空格和点
|
||||
safeName = safeName.trim().replace(/^\.+|\.+$/g, '');
|
||||
@@ -167,11 +238,22 @@ class FileManager {
|
||||
safeName = 'Untitled';
|
||||
}
|
||||
|
||||
// 限制长度,避免路径过长
|
||||
// 限制长度,避免路径过长(Windows路径限制为260字符)
|
||||
if (safeName.length > 100) {
|
||||
safeName = safeName.substring(0, 100);
|
||||
}
|
||||
|
||||
// 确保不以数字开头(避免与Windows保留名称冲突)
|
||||
if (/^\d/.test(safeName)) {
|
||||
safeName = 'artwork_' + safeName;
|
||||
}
|
||||
|
||||
// 检查是否为Windows保留名称
|
||||
const reservedNames = ['CON', 'PRN', 'AUX', 'NUL', 'COM1', 'COM2', 'COM3', 'COM4', 'COM5', 'COM6', 'COM7', 'COM8', 'COM9', 'LPT1', 'LPT2', 'LPT3', 'LPT4', 'LPT5', 'LPT6', 'LPT7', 'LPT8', 'LPT9'];
|
||||
if (reservedNames.includes(safeName.toUpperCase())) {
|
||||
safeName = 'artwork_' + safeName;
|
||||
}
|
||||
|
||||
return safeName;
|
||||
}
|
||||
|
||||
@@ -179,7 +261,7 @@ class FileManager {
|
||||
* 确保目录存在
|
||||
*/
|
||||
async ensureDirectory(dirPath) {
|
||||
const success = await FileUtils.safeEnsureDir(dirPath);
|
||||
const success = await FileUtils.safeEnsureDirEnhanced(dirPath);
|
||||
if (!success) {
|
||||
throw new Error(`目录创建失败: ${dirPath}`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user