@lynker-desktop/electron-sdk 0.0.9-alpha.87 → 0.0.9-alpha.89
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/main/downloader.d.ts +17 -14
- package/esm/main/downloader.d.ts.map +1 -1
- package/esm/main/downloader.js +421 -185
- package/esm/main/downloader.js.map +1 -1
- package/main/downloader.d.ts +17 -14
- package/main/downloader.d.ts.map +1 -1
- package/main/downloader.js +421 -185
- package/main/downloader.js.map +1 -1
- package/package.json +5 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"downloader.js","sources":["../../src/main/downloader.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport mime from 'mime-types';\nimport { DownloaderHelper } from 'node-downloader-helper';\nimport { ipcMain, dialog, BrowserWindow, app } from 'electron';\n\n/**\n * 最大重定向次数\n */\nconst MAX_REDIRECTS = 5;\n\n/**\n * 默认文件扩展名\n */\nconst DEFAULT_EXT = '.bin';\n\n/**\n * 默认 MIME 类型\n */\nconst DEFAULT_MIME_TYPE = 'application/octet-stream';\n\n/**\n * 默认超时时间(毫秒):10 分钟\n */\nconst DEFAULT_TIMEOUT = 600000;\n\n/**\n * 默认重试配置\n */\nconst DEFAULT_RETRY = {\n maxRetries: 3,\n delay: 1000\n};\n\n/**\n * 默认文件名\n */\nconst DEFAULT_FILENAME = 'download';\n\n/**\n * 最大文件名计数器(防止无限循环)\n */\nconst MAX_FILENAME_COUNTER = 10000;\n\n/**\n * 下载进度回调函数类型\n */\nexport type DownloadProgressCallback = (progress: {\n url: string; // 正在下载的 URL\n downloaded: number; // 已下载字节数\n total: number; // 总字节数(如果服务器提供 Content-Length)\n percentage: number; // 下载进度百分比 (0-100),如果 total 未知则为 -1\n speed: number; // 下载速度(字节/秒)\n}) => void;\n\n/**\n * 下载选项\n */\nexport interface DownloadOptions {\n /** 唯一标识(IPC 调用时必需) */\n id?: string;\n /** 输出文件路径(可选,如果不提供则使用 URL 的文件名) */\n outputPath?: string;\n /** 输出目录(可选,如果不提供则使用当前目录) */\n outputDir?: string;\n /** 输出文件名(可选,如果不提供则从 URL 提取) */\n fileName?: string;\n /** 是否覆盖已存在的文件(默认 true) */\n override?: boolean;\n /** 是否弹窗选择保存路径(默认 false) */\n showSaveDialog?: boolean;\n /** 保存对话框的默认文件名(可选) */\n defaultPath?: string;\n /** 超时时间(毫秒,默认 10 分钟) */\n timeout?: number;\n /** 重试配置 */\n retry?: {\n maxRetries?: number;\n delay?: number;\n };\n /** 下载进度回调函数(可选) */\n onProgress?: DownloadProgressCallback;\n /** 下载完成回调函数(可选) */\n onComplete?: (filePath: string) => void;\n /** 下载错误回调函数(可选) */\n onError?: (error: Error) => void;\n /** HTTP 请求选项 */\n httpRequestOptions?: {\n headers?: Record<string, string>;\n followRedirect?: boolean;\n maxRedirects?: number;\n };\n}\n\n/**\n * 下载结果\n */\nexport interface DownloadResult {\n /** 是否成功 */\n success: boolean;\n /** 下载的文件路径(成功时返回) */\n filePath?: string;\n /** 错误信息(失败时返回,包含错误类型和详细描述) */\n error?: string;\n /** 错误类型(失败时返回,用于区分不同类型的错误) */\n errorType?: 'NETWORK' | 'FILE_SYSTEM' | 'PERMISSION' | 'VALIDATION' | 'USER_CANCEL' | 'UNKNOWN';\n /** 文件大小(字节,成功时返回) */\n size?: number;\n}\n\n/**\n * 下载器类:提供文件下载功能\n */\nexport class Downloader {\n /** 正在下载的 URL 集合(避免重复下载) */\n private _downloadingUrls = new Map<string, DownloaderHelper>();\n /** 上一次选择的保存文件夹路径 */\n private _lastSelectedDir: string | null = null;\n\n /**\n * 构造函数:初始化下载器并注册 IPC 处理器\n * 注册 `core:downloader` IPC 处理器,用于从渲染进程接收下载请求\n */\n constructor() {\n ipcMain.handle(`core:downloader`, async (event: Electron.IpcMainInvokeEvent, options: DownloadOptions & { url: string; id: string }) => {\n try {\n const { id, url } = options;\n const sender = event.sender as Electron.WebContents;\n\n // 创建进度回调,通过 IPC 发送进度更新到渲染进程\n const progressCallback: DownloadProgressCallback = (progress) => {\n try {\n if (sender && typeof sender.send === 'function') {\n sender.send(`core:downloader:progress`, {\n id,\n data: progress\n });\n }\n } catch (error) {\n // 进度回调失败不影响下载,只记录日志\n console.error(`[Downloader] 发送下载进度失败 (ID: ${id}):`, error);\n }\n };\n\n const result = await this.download(url, options, progressCallback);\n return result;\n } catch (error) {\n // IPC 调用异常处理:解析错误并返回明确的错误信息\n const parsedError = this._parseError(error, options?.url);\n return {\n success: false,\n error: parsedError.message,\n errorType: parsedError.type\n };\n }\n });\n }\n\n /**\n * 下载文件\n *\n * 主要流程:\n * 1. 检查是否正在下载(避免重复下载)\n * 2. 确定输出路径(支持弹窗选择或自动解析)\n * 3. 验证并准备输出目录\n * 4. 处理 base64 data URL 或普通 HTTP 下载\n * 5. 使用临时文件下载,完成后重命名为最终文件\n *\n * @param url 要下载的 URL(支持 HTTP/HTTPS 和 base64 data URL)\n * @param options 下载选项(输出路径、重试配置、回调函数等)\n * @param onProgress 进度回调函数(可选,优先级高于 options.onProgress)\n * @returns Promise<DownloadResult> 下载结果,包含成功/失败状态、文件路径、错误信息等\n */\n public async download(\n url: string,\n options: DownloadOptions = {},\n onProgress?: DownloadProgressCallback\n ): Promise<DownloadResult> {\n // 参数验证:检查 URL 是否有效\n if (!url || typeof url !== 'string' || url.trim().length === 0) {\n return this._createErrorResult('下载 URL 不能为空', 'VALIDATION');\n }\n\n // 生成唯一标识:URL + ID(如果存在),用于避免重复下载\n const downloadKey = this._getDownloadKey(url, options.id);\n\n // 检查是否正在下载,避免重复下载同一资源\n if (this._downloadingUrls.has(downloadKey)) {\n return this._createErrorResult(\n `资源正在下载中,请勿重复下载: ${url}${options.id ? ` (ID: ${options.id})` : ''}`,\n 'VALIDATION'\n );\n }\n\n // 合并进度回调:优先使用传入的 onProgress,其次使用 options.onProgress\n const progressCallback = onProgress || options.onProgress;\n\n // 先检查是否是 base64 data URL(用于确定默认扩展名和特殊处理)\n const base64Info = this._isBase64DataUrl(url);\n const defaultExt = base64Info.isBase64 && base64Info.ext ? base64Info.ext : undefined;\n\n // 确定输出路径:支持弹窗选择或自动解析\n let outputPath: string;\n try {\n if (options.showSaveDialog) {\n // 显示保存对话框,让用户选择保存位置\n const savePath = await this._showSaveDialog(url, options, defaultExt);\n if (!savePath) {\n return this._createErrorResult('用户取消了保存操作', 'USER_CANCEL');\n }\n outputPath = savePath;\n } else {\n // 自动解析输出路径\n outputPath = this._resolveOutputPath(url, options, defaultExt);\n }\n\n // 验证 outputPath 是否有效\n if (!outputPath || typeof outputPath !== 'string' || outputPath.trim().length === 0) {\n return this._createErrorResult(\n '无法确定输出路径:路径解析结果无效',\n 'VALIDATION'\n );\n }\n } catch (error) {\n // 路径解析失败:可能是文件系统错误或权限问题\n const parsedError = this._parseError(error, url);\n return this._createErrorResult(\n `确定输出路径失败: ${parsedError.message}`,\n parsedError.type\n );\n }\n\n // 准备输出路径:确保目录存在、检查文件是否已存在\n try {\n const pathCheckResult = await this._prepareOutputPath(outputPath, options);\n if (pathCheckResult) {\n // 文件已存在且不允许覆盖,直接返回已存在的文件信息\n return pathCheckResult;\n }\n } catch (error) {\n // 路径准备失败:可能是权限问题或文件系统错误\n const parsedError = this._parseError(error, url);\n return this._createErrorResult(\n `准备输出路径失败: ${parsedError.message}`,\n parsedError.type\n );\n }\n\n // 提取下载目录和文件名\n let downloadDir = path.dirname(outputPath);\n let downloadFileName = path.basename(outputPath);\n\n // 再次清理文件名,确保不包含 query 参数和特殊字符(防止文件系统错误)\n downloadFileName = this._cleanFileName(downloadFileName);\n\n // 验证下载目录是否可访问,如果不可用则使用系统下载目录作为备用\n if (!this._isDirectoryAccessible(downloadDir)) {\n try {\n downloadDir = app.getPath('downloads');\n // 确保备用目录存在(如果不存在则创建)\n if (!fs.existsSync(downloadDir)) {\n fs.mkdirSync(downloadDir, { recursive: true });\n }\n // 验证备用目录是否可访问\n if (!this._isDirectoryAccessible(downloadDir)) {\n return this._createErrorResult(\n `无法访问下载目录: ${downloadDir},请检查目录权限`,\n 'PERMISSION'\n );\n }\n } catch (error) {\n const parsedError = this._parseError(error, url);\n return this._createErrorResult(\n `无法创建或访问备用下载目录: ${parsedError.message}`,\n parsedError.type\n );\n }\n }\n\n // 更新 outputPath 使用清理后的文件名和验证后的目录\n outputPath = path.join(downloadDir, downloadFileName);\n\n // 如果是 base64 data URL,使用已确定的输出路径进行特殊处理(无需网络请求)\n if (base64Info.isBase64 && base64Info.ext && base64Info.mimeType && base64Info.data) {\n return await this._handleBase64Download(\n url,\n { ext: base64Info.ext, mimeType: base64Info.mimeType, data: base64Info.data },\n outputPath,\n options,\n progressCallback\n );\n }\n\n // 使用临时文件下载(.cache 后缀),避免下载中断时文件不完整\n // 下载完成后会将临时文件重命名为最终文件\n const tempFilePath = path.join(downloadDir, downloadFileName + '.cache');\n\n return new Promise<DownloadResult>((resolve, reject) => {\n // 创建下载器实例(使用 node-downloader-helper)\n const dl = new DownloaderHelper(url, downloadDir, {\n fileName: downloadFileName + '.cache',\n retry: {\n maxRetries: options.retry?.maxRetries ?? DEFAULT_RETRY.maxRetries,\n delay: options.retry?.delay ?? DEFAULT_RETRY.delay\n },\n timeout: options.timeout ?? DEFAULT_TIMEOUT,\n override: true, // 临时文件总是覆盖,避免残留文件\n httpRequestOptions: {\n followRedirect: options.httpRequestOptions?.followRedirect ?? true,\n maxRedirects: options.httpRequestOptions?.maxRedirects ?? MAX_REDIRECTS,\n headers: options.httpRequestOptions?.headers\n }\n });\n\n // 记录正在下载的 URL(使用 URL + ID 作为 key),用于避免重复下载\n this._downloadingUrls.set(downloadKey, dl);\n\n // 监听下载进度事件\n if (progressCallback) {\n dl.on('progress', (stats) => {\n try {\n progressCallback({\n url,\n downloaded: stats.downloaded || 0,\n total: stats.total || 0,\n percentage: stats.progress !== undefined ? Math.round(stats.progress) : -1,\n speed: stats.speed || 0\n });\n } catch (error) {\n // 进度回调失败不影响下载,只记录日志\n console.error(`[Downloader] 进度回调执行失败 (${url}):`, error);\n }\n });\n }\n\n // 监听下载完成事件\n dl.on('end', () => {\n // 从下载列表中移除\n this._downloadingUrls.delete(downloadKey);\n\n // 将临时文件重命名为最终文件(原子操作,确保文件完整性)\n fs.rename(tempFilePath, outputPath, (renameErr) => {\n if (renameErr) {\n // 文件重命名失败:可能是权限问题或文件被占用\n const parsedError = this._parseError(renameErr, url);\n const error = new Error(\n `文件重命名失败: 无法将临时文件 \"${tempFilePath}\" 重命名为 \"${outputPath}\"。${parsedError.message}`\n );\n\n // 调用错误回调\n if (options.onError) {\n options.onError(error);\n }\n\n // reject 应该传入 Error 对象,而不是 DownloadResult\n // 将错误类型附加到错误对象上\n (error as any).errorType = parsedError.type;\n reject(error);\n } else {\n try {\n // 获取文件大小并返回成功结果\n const stats = fs.statSync(outputPath);\n const result = this._createSuccessResult(outputPath, stats.size);\n\n // 调用完成回调\n if (options.onComplete) {\n options.onComplete(outputPath);\n }\n\n resolve(result);\n } catch (error) {\n // 获取文件信息失败\n const parsedError = this._parseError(error, url);\n const errorObj = new Error(`下载完成但无法获取文件信息: ${parsedError.message}`);\n // 将错误类型附加到错误对象上\n (errorObj as any).errorType = parsedError.type;\n reject(errorObj);\n }\n }\n });\n });\n\n // 统一的错误处理函数\n const handleError = (error: Error | unknown, isUserCancel: boolean = false) => {\n // 从下载列表中移除\n this._downloadingUrls.delete(downloadKey);\n\n // 清理临时文件(忽略删除失败,避免影响错误处理)\n fs.promises.unlink(tempFilePath).catch(() => {\n // 临时文件删除失败不影响错误处理流程\n });\n\n // 解析错误信息\n const parsedError = this._parseError(error, url);\n const errorType = isUserCancel ? 'USER_CANCEL' : parsedError.type;\n const errorMessage = isUserCancel\n ? `下载已取消: ${url}`\n : parsedError.message;\n\n // 创建错误对象(包含错误类型信息)\n const errorObj = new Error(errorMessage);\n // 将错误类型附加到错误对象上,供 catch 处理时使用\n (errorObj as any).errorType = errorType;\n\n // 调用错误回调\n if (options.onError) {\n options.onError(errorObj);\n }\n\n // reject 应该传入 Error 对象,而不是 DownloadResult\n reject(errorObj);\n };\n\n // 监听下载错误事件(网络错误、服务器错误等)\n dl.on('error', (err) => {\n const error = err instanceof Error ? err : new Error(`下载失败: ${url}`);\n handleError(error, false);\n });\n\n // 监听下载停止事件(用户取消或手动停止)\n dl.on('stop', () => {\n const error = new Error(`下载已停止: ${url}`);\n handleError(error, true);\n });\n\n // 开始下载\n dl.start().catch((err) => {\n // 启动下载失败:可能是 URL 无效、网络不可达等\n const error = err instanceof Error ? err : new Error(`启动下载失败: ${url}`);\n handleError(error, false);\n });\n }).catch((error): DownloadResult => {\n // Promise 异常捕获:返回格式化的错误结果\n // 如果错误对象已经包含 errorType,直接使用;否则解析错误\n if (error instanceof Error && (error as any).errorType) {\n return this._createErrorResult(error.message, (error as any).errorType);\n }\n const parsedError = this._parseError(error, url);\n return this._createErrorResult(parsedError.message, parsedError.type);\n });\n }\n\n /**\n * 生成下载任务的唯一标识\n * @param url 下载 URL\n * @param id 可选的任务 ID\n * @returns 唯一标识\n */\n private _getDownloadKey(url: string, id?: string): string {\n return id ? `${url}::${id}` : url;\n }\n\n /**\n * 取消下载\n * @param url 要取消的 URL\n * @param id 可选的任务 ID\n * @returns 是否成功取消\n */\n public cancel(url: string, id?: string): boolean {\n const downloadKey = this._getDownloadKey(url, id);\n const dl = this._downloadingUrls.get(downloadKey);\n if (dl) {\n dl.stop();\n this._downloadingUrls.delete(downloadKey);\n return true;\n }\n return false;\n }\n\n /**\n * 取消所有正在进行的下载\n */\n public cancelAll(): void {\n for (const [url, dl] of this._downloadingUrls.entries()) {\n dl.stop();\n }\n this._downloadingUrls.clear();\n }\n\n /**\n * 检查指定 URL 是否正在下载\n * @param url 要检查的 URL\n * @param id 可选的任务 ID\n * @returns 是否正在下载\n */\n public isDownloading(url: string, id?: string): boolean {\n const downloadKey = this._getDownloadKey(url, id);\n return this._downloadingUrls.has(downloadKey);\n }\n\n /**\n * 获取正在下载的任务列表\n * @returns 正在下载的任务信息数组,包含 URL 和 ID(如果有)\n */\n public getDownloadingUrls(): Array<{ url: string; id?: string }> {\n return Array.from(this._downloadingUrls.keys()).map(key => {\n const parts = key.split('::');\n if (parts.length === 2 && parts[0] && parts[1]) {\n return { url: parts[0], id: parts[1] };\n }\n return { url: key };\n });\n }\n\n /**\n * 获取文件名(统一处理所有来源)\n * @param url 下载 URL\n * @param options 下载选项\n * @param defaultExt 默认扩展名(可选,用于 base64 URL)\n * @returns 清理后的文件名\n */\n private _getFileName(url: string, options: DownloadOptions, defaultExt?: string): string {\n // 优先级:defaultPath > fileName > defaultExt > URL提取\n if (options.defaultPath) {\n return this._cleanFileName(path.basename(options.defaultPath));\n }\n\n if (options.fileName) {\n return this._cleanFileName(options.fileName);\n }\n\n if (defaultExt) {\n return this._cleanFileName(`${DEFAULT_FILENAME}.${defaultExt}`);\n }\n // 从 URL 中提取文件名(_extractFileNameFromUrl 已经包含清理逻辑)\n return this._extractFileNameFromUrl(url);\n }\n\n /**\n * 检查目录是否存在且有访问权限\n *\n * 检查项:\n * 1. 路径是否有效(非空字符串)\n * 2. 目录是否存在\n * 3. 路径是否为目录(而非文件)\n * 4. 是否有读写权限\n *\n * @param dirPath 目录路径\n * @returns 如果目录存在且有读写权限则返回 true,否则返回 false\n */\n private _isDirectoryAccessible(dirPath: string): boolean {\n // 参数验证:检查路径是否有效\n if (!dirPath || typeof dirPath !== 'string') {\n return false;\n }\n\n try {\n // 检查目录是否存在\n if (!fs.existsSync(dirPath)) {\n return false;\n }\n\n // 检查是否是目录(而非文件)\n const stats = fs.statSync(dirPath);\n if (!stats.isDirectory()) {\n return false;\n }\n\n // 检查是否有读写权限(R_OK: 读权限, W_OK: 写权限)\n fs.accessSync(dirPath, fs.constants.R_OK | fs.constants.W_OK);\n return true;\n } catch {\n // 如果任何检查失败(不存在、不是目录、无权限等),返回 false\n return false;\n }\n }\n\n /**\n * 显示保存对话框\n *\n * 功能说明:\n * 1. 确定默认保存目录(优先级:defaultPath > outputDir > _lastSelectedDir > 系统下载目录)\n * 2. 验证并更新 _lastSelectedDir(如果不可访问则重置)\n * 3. 生成唯一文件名(如果文件已存在,添加数字后缀)\n * 4. 显示 Electron 保存对话框\n * 5. 记录用户选择的目录(用于下次默认使用)\n *\n * @param url 下载 URL(用于提取默认文件名)\n * @param options 下载选项(包含 defaultPath、outputDir 等)\n * @param defaultExt 默认扩展名(可选,用于 base64 URL)\n * @returns Promise<string | null> 用户选择的保存路径,如果取消则返回 null\n */\n private async _showSaveDialog(url: string, options: DownloadOptions, defaultExt?: string): Promise<string | null> {\n // 获取并清理默认文件名\n let defaultFileName = this._getFileName(url, options, defaultExt);\n\n // 获取默认目录\n let defaultDir: string | undefined;\n if (options.defaultPath) {\n defaultDir = path.dirname(options.defaultPath);\n } else if (options.outputDir) {\n defaultDir = path.resolve(options.outputDir);\n } else {\n // 如果没有指定目录,使用上一次选择的文件夹,如果没有则使用下载目录\n // 检查 _lastSelectedDir 是否可用,如果不可用则重置为下载目录\n if (this._lastSelectedDir && !this._isDirectoryAccessible(this._lastSelectedDir)) {\n const downloadsPath = app.getPath('downloads');\n // 验证下载目录是否可用,如果可用则使用,否则设为 null\n this._lastSelectedDir = this._isDirectoryAccessible(downloadsPath) ? downloadsPath : null;\n }\n defaultDir = this._lastSelectedDir || app.getPath('downloads');\n }\n\n // 如果指定了目录,检查文件是否存在并生成唯一文件名\n if (defaultDir) {\n defaultFileName = this._generateUniqueFileName(defaultDir, defaultFileName);\n }\n\n // 构建默认路径\n const defaultPath = defaultDir ? path.join(defaultDir, defaultFileName) : defaultFileName;\n\n // 获取当前活动的 BrowserWindow\n const focusedWindow = BrowserWindow.getFocusedWindow();\n const allWindows = BrowserWindow.getAllWindows();\n const parentWindow = focusedWindow || (allWindows.length > 0 ? allWindows[0] : undefined);\n\n // 显示保存对话框\n const dialogOptions = {\n defaultPath,\n title: '保存文件',\n buttonLabel: '保存',\n filters: [\n // 尝试从文件名推断文件类型\n ...(path.extname(defaultFileName) ? [{\n name: '所有文件',\n extensions: ['*']\n }] : [])\n ]\n };\n\n // 根据是否有父窗口调用不同的方法\n const result = parentWindow\n ? await dialog.showSaveDialog(parentWindow, dialogOptions)\n : await dialog.showSaveDialog(dialogOptions);\n\n if (result.canceled || !result.filePath) {\n return null;\n }\n\n // 记录用户选择的文件夹路径,用于下次默认使用\n const selectedDir = path.dirname(result.filePath);\n if (selectedDir) {\n // 验证选择的目录是否可访问,只有可访问时才保存\n if (this._isDirectoryAccessible(selectedDir)) {\n this._lastSelectedDir = selectedDir;\n } else {\n // 如果选择的目录不可访问,尝试使用下载目录\n const downloadsPath = app.getPath('downloads');\n this._lastSelectedDir = this._isDirectoryAccessible(downloadsPath) ? downloadsPath : null;\n }\n }\n\n return result.filePath;\n }\n\n /**\n * 检测并处理 base64 data URL\n * @param url 资源URL\n * @returns 如果是 base64 URL,返回 true、文件扩展名、MIME 类型和数据;否则返回 false\n */\n private _isBase64DataUrl(url: string): { isBase64: boolean, ext?: string, mimeType?: string, data?: string } {\n if (!url.startsWith('data:')) {\n return { isBase64: false };\n }\n\n try {\n // 解析 data URL 格式:data:[<mediatype>][;base64],<data>\n const commaIndex = url.indexOf(',');\n if (commaIndex === -1) {\n return { isBase64: false };\n }\n\n const header = url.substring(0, commaIndex);\n const data = url.substring(commaIndex + 1);\n\n // 检查是否包含 base64 标识\n if (!header.includes('base64')) {\n return { isBase64: false };\n }\n\n // 从 mediatype 中提取文件扩展名和 MIME 类型\n // 例如:data:image/png;base64 -> png, image/png\n // 例如:data:image/jpeg;base64 -> jpeg, image/jpeg\n let ext = DEFAULT_EXT.replace(/^\\./, '');\n let mimeType = DEFAULT_MIME_TYPE;\n const mimeMatch = header.match(/data:([^;]+)/);\n if (mimeMatch && mimeMatch[1]) {\n mimeType = mimeMatch[1];\n // 使用 mime-types 包从 MIME 类型获取扩展名\n const extension = mime.extension(mimeType);\n if (extension) {\n ext = extension;\n }\n }\n\n return { isBase64: true, ext, mimeType, data };\n } catch (error) {\n return { isBase64: false };\n }\n }\n\n /**\n * 保存 base64 数据到文件\n *\n * 处理流程:\n * 1. 将 base64 字符串解码为 Buffer\n * 2. 将 Buffer 写入目标文件路径\n *\n * @param base64Data base64 编码的数据字符串\n * @param filePath 目标文件路径(完整路径,包含文件名)\n * @throws 如果解码失败或文件写入失败,抛出错误\n */\n private async _saveBase64ToFile(base64Data: string, filePath: string): Promise<void> {\n try {\n // 解码 base64 数据为 Buffer\n const buffer = Buffer.from(base64Data, 'base64');\n\n // 使用 Promise 版本的 writeFile 写入文件\n // Buffer 继承自 Uint8Array,可以直接使用\n await fs.promises.writeFile(filePath, buffer as Uint8Array);\n } catch (error) {\n // 文件保存失败:可能是权限问题、磁盘空间不足、路径无效等\n const errorMessage = error instanceof Error ? error.message : '未知错误';\n throw new Error(`保存 base64 文件失败 (${filePath}): ${errorMessage}`);\n }\n }\n\n /**\n * 准备输出路径\n *\n * 主要功能:\n * 1. 确保输出目录存在(不存在则创建)\n * 2. 检查文件是否已存在\n * 3. 如果文件已存在且不允许覆盖,返回已存在的文件信息\n *\n * @param outputPath 输出文件路径\n * @param options 下载选项(包含 override 选项)\n * @returns 如果文件已存在且不允许覆盖,返回已存在的文件信息;否则返回 null\n * @throws 如果目录创建失败或权限不足,抛出错误\n */\n private async _prepareOutputPath(\n outputPath: string,\n options: DownloadOptions\n ): Promise<DownloadResult | null> {\n // 参数验证:确保 outputPath 是有效的非空字符串\n if (!outputPath || typeof outputPath !== 'string' || outputPath.trim().length === 0) {\n throw new Error(`输出路径无效: ${outputPath}`);\n }\n\n // 规范化路径(解析相对路径、处理 .. 等)\n const normalizedPath = path.resolve(outputPath);\n\n // 验证规范化后的路径是否有效\n if (!normalizedPath || normalizedPath.trim().length === 0) {\n throw new Error(`无法解析输出路径: ${outputPath}`);\n }\n\n const downloadDir = path.dirname(normalizedPath);\n\n // 使用规范化后的路径\n outputPath = normalizedPath;\n\n // 确保目录存在:如果不存在则创建(递归创建父目录)\n if (!fs.existsSync(downloadDir)) {\n try {\n fs.mkdirSync(downloadDir, { recursive: true });\n } catch (error) {\n // 目录创建失败:可能是权限问题或路径无效\n const parsedError = this._parseError(error);\n throw new Error(`无法创建输出目录 \"${downloadDir}\": ${parsedError.message}`);\n }\n }\n\n // 验证目录是否可访问\n if (!this._isDirectoryAccessible(downloadDir)) {\n throw new Error(`无法访问输出目录 \"${downloadDir}\",请检查目录权限`);\n }\n\n // 如果文件已存在且不允许覆盖,直接返回已存在的文件信息\n if (fs.existsSync(outputPath) && !(options.override ?? true)) {\n try {\n const stats = fs.statSync(outputPath);\n return this._createSuccessResult(outputPath, stats.size);\n } catch (error) {\n // 获取文件信息失败:可能是权限问题\n const parsedError = this._parseError(error);\n throw new Error(`无法访问已存在的文件 \"${outputPath}\": ${parsedError.message}`);\n }\n }\n\n return null;\n }\n\n /**\n * 创建成功结果\n * @param filePath 文件路径\n * @param size 文件大小(字节)\n * @returns DownloadResult 成功结果对象\n */\n private _createSuccessResult(filePath: string, size: number): DownloadResult {\n return {\n success: true,\n filePath,\n size\n };\n }\n\n /**\n * 创建错误结果\n * @param error 错误信息\n * @param errorType 错误类型(可选,用于区分不同类型的错误)\n * @returns DownloadResult 错误结果对象\n */\n private _createErrorResult(error: string, errorType?: DownloadResult['errorType']): DownloadResult {\n return {\n success: false,\n error,\n errorType: errorType || 'UNKNOWN'\n };\n }\n\n /**\n * 从错误对象中提取错误类型和详细信息\n * @param error 错误对象\n * @param url 下载的 URL(用于上下文信息)\n * @returns 包含错误类型和详细错误信息的对象\n */\n private _parseError(error: unknown, url?: string): { type: DownloadResult['errorType']; message: string } {\n if (error instanceof Error) {\n const errorMessage = error.message.toLowerCase();\n const errorCode = (error as any).code?.toLowerCase() || '';\n\n // 网络相关错误\n if (\n errorMessage.includes('network') ||\n errorMessage.includes('timeout') ||\n errorMessage.includes('econnrefused') ||\n errorMessage.includes('enotfound') ||\n errorMessage.includes('etimedout') ||\n errorCode.includes('timeout') ||\n errorCode.includes('econnrefused') ||\n errorCode.includes('enotfound') ||\n errorCode.includes('etimedout')\n ) {\n return {\n type: 'NETWORK',\n message: `网络错误: ${error.message}${url ? ` (URL: ${url})` : ''}`\n };\n }\n\n // 文件系统相关错误\n if (\n errorMessage.includes('enoent') ||\n errorMessage.includes('eacces') ||\n errorMessage.includes('eperm') ||\n errorMessage.includes('file') ||\n errorMessage.includes('directory') ||\n errorMessage.includes('path') ||\n errorCode.includes('enoent') ||\n errorCode.includes('eacces') ||\n errorCode.includes('eperm')\n ) {\n if (errorMessage.includes('permission') || errorMessage.includes('eacces') || errorMessage.includes('eperm')) {\n return {\n type: 'PERMISSION',\n message: `权限错误: ${error.message}${url ? ` (URL: ${url})` : ''}`\n };\n }\n return {\n type: 'FILE_SYSTEM',\n message: `文件系统错误: ${error.message}${url ? ` (URL: ${url})` : ''}`\n };\n }\n\n // 验证相关错误\n if (\n errorMessage.includes('invalid') ||\n errorMessage.includes('validation') ||\n errorMessage.includes('格式') ||\n errorMessage.includes('无效')\n ) {\n return {\n type: 'VALIDATION',\n message: `验证错误: ${error.message}${url ? ` (URL: ${url})` : ''}`\n };\n }\n\n // 用户取消\n if (errorMessage.includes('cancel') || errorMessage.includes('取消')) {\n return {\n type: 'USER_CANCEL',\n message: error.message\n };\n }\n\n // 其他错误\n return {\n type: 'UNKNOWN',\n message: `未知错误: ${error.message}${url ? ` (URL: ${url})` : ''}`\n };\n }\n\n // 非 Error 对象\n return {\n type: 'UNKNOWN',\n message: `未知错误: ${String(error)}${url ? ` (URL: ${url})` : ''}`\n };\n }\n\n /**\n * 处理 base64 data URL 的下载\n *\n * base64 data URL 格式:data:[<mediatype>][;base64],<data>\n * 例如:data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...\n *\n * 处理流程:\n * 1. 验证 base64 数据有效性\n * 2. 触发进度回调(模拟 100% 完成)\n * 3. 解码并保存 base64 数据到文件\n * 4. 返回成功结果\n *\n * @param url base64 data URL\n * @param base64Info base64 信息(扩展名、MIME 类型、数据)\n * @param outputPath 已确定的输出路径\n * @param options 下载选项\n * @param progressCallback 进度回调(可选)\n * @returns Promise<DownloadResult> 下载结果\n */\n private async _handleBase64Download(\n url: string,\n base64Info: { ext: string; mimeType: string; data: string },\n outputPath: string,\n options: DownloadOptions,\n progressCallback?: DownloadProgressCallback\n ): Promise<DownloadResult> {\n // 验证 base64 数据有效性\n if (!base64Info.data || !base64Info.ext) {\n return this._createErrorResult(\n `无效的 base64 data URL: 缺少数据或扩展名 (URL: ${url.substring(0, 50)}...)`,\n 'VALIDATION'\n );\n }\n\n try {\n // 计算数据长度(用于进度回调)\n const dataLength = Buffer.from(base64Info.data, 'base64').length;\n\n // 模拟进度回调(base64 数据通常很小,立即完成)\n if (progressCallback) {\n progressCallback({\n url,\n downloaded: dataLength,\n total: dataLength,\n percentage: 100,\n speed: 0\n });\n }\n\n // 保存 base64 数据到文件\n await this._saveBase64ToFile(base64Info.data, outputPath);\n\n // 获取文件大小并返回成功结果\n const stats = fs.statSync(outputPath);\n const result = this._createSuccessResult(outputPath, stats.size);\n\n // 调用完成回调\n if (options.onComplete) {\n options.onComplete(outputPath);\n }\n\n return result;\n } catch (error) {\n // base64 文件保存失败:可能是文件系统错误或权限问题\n const parsedError = this._parseError(error, url);\n const err = error instanceof Error ? error : new Error(`保存 base64 文件失败: ${parsedError.message}`);\n\n // 调用错误回调\n if (options.onError) {\n options.onError(err);\n }\n\n return this._createErrorResult(\n `保存 base64 文件失败: ${parsedError.message}`,\n parsedError.type\n );\n }\n }\n\n /**\n * 生成唯一的文件名(如果文件已存在,添加数字后缀)\n *\n * 命名规则:\n * - 如果文件不存在,返回原始文件名\n * - 如果文件已存在,添加数字后缀:filename(1).ext, filename(2).ext, ...\n * - 最多尝试 MAX_FILENAME_COUNTER 次,防止无限循环\n *\n * 示例:\n * - \"test.pdf\" -> \"test.pdf\" (如果不存在)\n * - \"test.pdf\" -> \"test(1).pdf\" (如果 test.pdf 已存在)\n * - \"test(1).pdf\" -> \"test(2).pdf\" (如果 test(1).pdf 也已存在)\n *\n * @param dir 目录路径\n * @param fileName 原始文件名(包含扩展名)\n * @returns 唯一的文件名(如果原文件不存在则返回原文件名,否则返回带数字后缀的文件名)\n */\n private _generateUniqueFileName(dir: string, fileName: string): string {\n // 分离文件名和扩展名\n const ext = path.extname(fileName);\n const nameWithoutExt = path.basename(fileName, ext);\n\n // 检查原始文件名是否存在\n const originalPath = path.join(dir, fileName);\n if (!fs.existsSync(originalPath)) {\n return fileName;\n }\n\n // 如果存在,尝试添加数字后缀\n let counter = 1;\n let newFileName: string;\n let newPath: string;\n\n do {\n newFileName = `${nameWithoutExt}(${counter})${ext}`;\n newPath = path.join(dir, newFileName);\n counter++;\n } while (fs.existsSync(newPath) && counter < MAX_FILENAME_COUNTER);\n\n return newFileName;\n }\n\n /**\n * 清理文件名,移除 query 参数和特殊字符\n * @param fileName 原始文件名\n * @returns 清理后的文件名\n */\n private _cleanFileName(fileName: string): string {\n if (!fileName || fileName.length === 0) {\n return DEFAULT_FILENAME;\n }\n\n // 移除 query 参数(如果文件名中包含 ?)\n const queryIndex = fileName.indexOf('?');\n if (queryIndex !== -1) {\n fileName = fileName.substring(0, queryIndex);\n }\n\n // 解码 URL 编码的字符(如 %20 -> 空格)\n try {\n fileName = decodeURIComponent(fileName);\n } catch {\n // 如果解码失败,使用原始文件名\n }\n\n // 清理文件名中的特殊字符(文件系统不允许的字符)\n // 移除或替换:/ \\ : * ? \" < > |\n fileName = fileName.replace(/[/\\\\:*?\"<>|]/g, '_');\n\n // 移除前后空格和点号(Windows 不允许)\n fileName = fileName.trim().replace(/^\\.+|\\.+$/g, '');\n\n // 如果清理后文件名为空,使用默认文件名\n if (!fileName || fileName.length === 0) {\n fileName = DEFAULT_FILENAME;\n }\n\n return fileName;\n }\n\n /**\n * 从 URL 中提取文件名\n * @param url 下载 URL\n * @returns 文件名(已清理特殊字符,不包含query参数)\n */\n private _extractFileNameFromUrl(url: string): string {\n if (!url || typeof url !== 'string' || url.trim().length === 0) {\n return `${DEFAULT_FILENAME}${DEFAULT_EXT}`;\n }\n\n try {\n // 确保 URL 是完整的(如果缺少协议,添加 https://)\n let normalizedUrl = url.trim();\n if (!normalizedUrl.match(/^https?:\\/\\//i)) {\n normalizedUrl = `https://${normalizedUrl}`;\n }\n\n const urlObj = new URL(normalizedUrl);\n\n // 验证 pathname 是否有效\n if (!urlObj.pathname || urlObj.pathname === '/' || urlObj.pathname.length === 0) {\n return `${DEFAULT_FILENAME}${DEFAULT_EXT}`;\n }\n\n // 只使用 pathname 部分,确保不包含 query 参数\n let fileName = path.basename(urlObj.pathname);\n\n // 如果 pathname 以 / 结尾或 basename 为空,尝试从路径中提取\n if (!fileName || fileName === '/' || fileName.length === 0) {\n // 移除首尾的斜杠,然后取最后一部分\n const pathParts = urlObj.pathname.replace(/^\\/+|\\/+$/g, '').split('/');\n fileName = pathParts[pathParts.length - 1] || DEFAULT_FILENAME;\n }\n\n // 如果还是没有有效的文件名,使用默认值\n if (!fileName || fileName === '/' || fileName.length === 0) {\n return `${DEFAULT_FILENAME}${DEFAULT_EXT}`;\n }\n\n // 解码 URL 编码的字符(如 %20 -> 空格)\n try {\n fileName = decodeURIComponent(fileName);\n } catch {\n // 如果解码失败,使用原始文件名\n }\n\n // 清理文件名中的特殊字符(文件系统不允许的字符)\n // 移除或替换:/ \\ : * ? \" < > |\n fileName = fileName.replace(/[/\\\\:*?\"<>|]/g, '_');\n\n // 移除前后空格和点号(Windows 不允许)\n fileName = fileName.trim().replace(/^\\.+|\\.+$/g, '');\n\n // 如果清理后文件名为空,使用默认文件名\n if (!fileName || fileName.length === 0) {\n fileName = DEFAULT_FILENAME;\n }\n\n // 确保有扩展名\n return path.extname(fileName) ? fileName : `${fileName}${DEFAULT_EXT}`;\n } catch (error) {\n // URL 解析失败,尝试手动提取\n try {\n // 移除 query 参数和 hash\n const urlWithoutQuery = url.split('?')[0];\n const cleanUrl = urlWithoutQuery ? urlWithoutQuery.split('#')[0] : url;\n if (cleanUrl) {\n // 尝试提取最后一个路径段\n const match = cleanUrl.match(/\\/([^\\/]+)$/);\n if (match && match[1]) {\n let fileName = match[1];\n // 清理文件名\n fileName = fileName.replace(/[/\\\\:*?\"<>|]/g, '_');\n fileName = fileName.trim().replace(/^\\.+|\\.+$/g, '');\n if (fileName && fileName.length > 0) {\n return path.extname(fileName) ? fileName : `${fileName}${DEFAULT_EXT}`;\n }\n }\n }\n } catch {\n // 忽略手动提取的错误\n }\n return `${DEFAULT_FILENAME}${DEFAULT_EXT}`;\n }\n }\n\n /**\n * 解析输出路径\n * @param url 下载 URL\n * @param options 下载选项\n * @param defaultExt 默认扩展名(可选,用于 base64 URL)\n * @returns 输出文件路径\n * @throws 如果无法解析有效路径,抛出错误\n */\n private _resolveOutputPath(url: string, options: DownloadOptions, defaultExt?: string): string {\n // 如果提供了完整路径,直接使用(但需要清理文件名部分)\n if (options.outputPath) {\n // 验证 outputPath 是否有效\n if (typeof options.outputPath !== 'string' || options.outputPath.trim().length === 0) {\n throw new Error(`无效的输出路径: ${options.outputPath}`);\n }\n\n try {\n const dir = path.dirname(options.outputPath);\n const fileName = this._cleanFileName(path.basename(options.outputPath));\n\n // 验证文件名是否有效\n if (!fileName || fileName.trim().length === 0) {\n throw new Error(`无法从路径中提取有效文件名: ${options.outputPath}`);\n }\n\n const resolvedPath = path.resolve(path.join(dir, fileName));\n\n // 验证解析后的路径是否有效\n if (!resolvedPath || resolvedPath.trim().length === 0) {\n throw new Error(`路径解析失败: ${options.outputPath}`);\n }\n\n return resolvedPath;\n } catch (error) {\n if (error instanceof Error) {\n throw error;\n }\n throw new Error(`解析输出路径失败: ${String(error)}`);\n }\n }\n\n // 确定输出目录:优先使用指定的目录,否则使用 Electron 的下载目录\n let outputDir: string;\n try {\n if (options.outputDir) {\n if (typeof options.outputDir !== 'string' || options.outputDir.trim().length === 0) {\n throw new Error(`无效的输出目录: ${options.outputDir}`);\n }\n outputDir = path.resolve(options.outputDir);\n } else {\n outputDir = app.getPath('downloads');\n }\n\n // 验证输出目录是否有效\n if (!outputDir || outputDir.trim().length === 0) {\n throw new Error('无法获取有效的输出目录');\n }\n } catch (error) {\n if (error instanceof Error) {\n throw error;\n }\n throw new Error(`确定输出目录失败: ${String(error)}`);\n }\n\n // 使用统一的文件名获取方法\n const fileName = this._getFileName(url, options, defaultExt);\n\n // 验证文件名是否有效\n if (!fileName || fileName.trim().length === 0) {\n throw new Error('无法获取有效的文件名');\n }\n\n const finalPath = path.join(outputDir, fileName);\n\n // 验证最终路径是否有效\n if (!finalPath || finalPath.trim().length === 0) {\n throw new Error('无法构建有效的输出路径');\n }\n\n return finalPath;\n }\n}\n\n/**\n * 默认下载器实例(单例)\n */\nlet defaultDownloader: Downloader | null = null;\n\n/**\n * 获取默认下载器实例\n * @returns Downloader 实例\n */\nexport function getDownloader(): Downloader {\n if (!defaultDownloader) {\n defaultDownloader = new Downloader();\n }\n return defaultDownloader;\n}\n\n/**\n * 下载文件的便捷函数\n * @param url 要下载的 URL\n * @param options 下载选项\n * @param onProgress 进度回调函数(可选)\n * @returns Promise<DownloadResult> 下载结果\n */\nexport async function downloadFile(\n url: string,\n options: DownloadOptions = {},\n onProgress?: DownloadProgressCallback\n): Promise<DownloadResult> {\n const downloader = getDownloader();\n return downloader.download(url, options, onProgress);\n}\n\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"downloader.js","sources":["../../src/main/downloader.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport mime from 'mime-types';\nimport { DownloaderHelper } from 'node-downloader-helper';\nimport { ipcMain, dialog, BrowserWindow, app } from 'electron';\n\n/**\n * 最大重定向次数\n */\nconst MAX_REDIRECTS = 5;\n\n/**\n * 默认文件扩展名\n */\nconst DEFAULT_EXT = '.bin';\n\n/**\n * 默认 MIME 类型\n */\nconst DEFAULT_MIME_TYPE = 'application/octet-stream';\n\n/**\n * 默认超时时间(毫秒):10 分钟\n */\nconst DEFAULT_TIMEOUT = 600000;\n\n/**\n * 默认重试配置\n */\nconst DEFAULT_RETRY = {\n maxRetries: 3,\n delay: 1000\n};\n\n/**\n * 默认文件名\n */\nconst DEFAULT_FILENAME = 'download';\n\n/**\n * 最大文件名计数器(防止无限循环)\n */\nconst MAX_FILENAME_COUNTER = 10000;\n\n/**\n * 下载进度回调函数类型\n */\nexport type DownloadProgressCallback = (progress: {\n url: string; // 正在下载的 URL\n downloaded: number; // 已下载字节数\n total: number; // 总字节数(如果服务器提供 Content-Length)\n percentage: number; // 下载进度百分比 (0-100),如果 total 未知则为 -1\n speed: number; // 下载速度(字节/秒)\n}) => void;\n\n/**\n * 下载选项\n */\nexport interface DownloadOptions {\n /** 唯一标识(IPC 调用时必需) */\n id?: string;\n /** 输出文件路径(可选,如果不提供则使用 URL 的文件名) */\n outputPath?: string;\n /** 输出目录(可选,如果不提供则使用当前目录) */\n outputDir?: string;\n /** 输出文件名(可选,如果不提供则从 URL 提取) */\n fileName?: string;\n /** 是否覆盖已存在的文件(默认 true) */\n override?: boolean;\n /** 是否弹窗选择保存路径(默认 false) */\n showSaveDialog?: boolean;\n /** 保存对话框的默认文件名(可选) */\n defaultPath?: string;\n /** 超时时间(毫秒,默认 10 分钟) */\n timeout?: number;\n /** 重试配置 */\n retry?: {\n maxRetries?: number;\n delay?: number;\n };\n /** 下载进度回调函数(可选) */\n onProgress?: DownloadProgressCallback;\n /** 下载完成回调函数(可选) */\n onComplete?: (filePath: string) => void;\n /** 下载错误回调函数(可选) */\n onError?: (error: Error) => void;\n /** HTTP 请求选项 */\n httpRequestOptions?: {\n headers?: Record<string, string>;\n followRedirect?: boolean;\n maxRedirects?: number;\n };\n}\n\n/**\n * 下载结果\n */\nexport interface DownloadResult {\n /** 是否成功 */\n success: boolean;\n /** 下载的文件路径(成功时返回) */\n filePath?: string;\n /** 错误信息(失败时返回,包含错误类型和详细描述) */\n error?: string;\n /** 错误类型(失败时返回,用于区分不同类型的错误) */\n errorType?: 'NETWORK' | 'FILE_SYSTEM' | 'PERMISSION' | 'VALIDATION' | 'USER_CANCEL' | 'UNKNOWN';\n /** 文件大小(字节,成功时返回) */\n size?: number;\n}\n\n/**\n * 下载器类:提供文件下载功能\n */\nexport class Downloader {\n /** 正在下载的 URL 集合(避免重复下载) */\n private _downloadingUrls = new Map<string, DownloaderHelper>();\n /** 上一次选择的保存文件夹路径 */\n private _lastSelectedDir: string | null = null;\n /** 缓存的下载目录路径 */\n private _cachedDownloadsPath: string | null = null;\n /** 目录可访问性缓存(避免重复检查) */\n private _directoryAccessCache = new Map<string, { accessible: boolean; timestamp: number }>();\n /** 目录缓存有效期(毫秒) */\n private readonly _cacheTTL = 60000; // 60秒\n\n /**\n * 构造函数:初始化下载器并注册 IPC 处理器\n * 注册 `core:downloader` IPC 处理器,用于从渲染进程接收下载请求\n */\n constructor() {\n ipcMain.handle(`core:downloader`, async (event: Electron.IpcMainInvokeEvent, options: DownloadOptions & { url: string; id: string }) => {\n try {\n const { id, url } = options;\n const sender = event.sender as Electron.WebContents;\n\n // 创建进度回调,通过 IPC 发送进度更新到渲染进程\n const progressCallback: DownloadProgressCallback = (progress) => {\n try {\n if (sender && typeof sender.send === 'function') {\n sender.send(`core:downloader:progress`, {\n id,\n data: progress\n });\n }\n } catch (error) {\n // 进度回调失败不影响下载,只记录日志\n console.error(`[Downloader] 发送下载进度失败 (ID: ${id}):`, error);\n }\n };\n\n const result = await this.download(url, options, progressCallback);\n return result;\n } catch (error) {\n // IPC 调用异常处理:解析错误并返回明确的错误信息\n const parsedError = this._parseError(error, options?.url);\n return {\n success: false,\n error: parsedError.message,\n errorType: parsedError.type\n };\n }\n });\n }\n\n /**\n * 下载文件\n *\n * 主要流程:\n * 1. 检查是否正在下载(避免重复下载)\n * 2. 确定输出路径(支持弹窗选择或自动解析)\n * 3. 验证并准备输出目录\n * 4. 处理 base64 data URL 或普通 HTTP 下载\n * 5. 使用临时文件下载,完成后重命名为最终文件\n *\n * @param url 要下载的 URL(支持 HTTP/HTTPS 和 base64 data URL)\n * @param options 下载选项(输出路径、重试配置、回调函数等)\n * @param onProgress 进度回调函数(可选,优先级高于 options.onProgress)\n * @returns Promise<DownloadResult> 下载结果,包含成功/失败状态、文件路径、错误信息等\n */\n public async download(\n url: string,\n options: DownloadOptions = {},\n onProgress?: DownloadProgressCallback\n ): Promise<DownloadResult> {\n // 参数验证:检查 URL 是否有效\n if (!url || typeof url !== 'string' || url.trim().length === 0) {\n return this._createErrorResult('下载 URL 不能为空', 'VALIDATION');\n }\n\n // 生成唯一标识:URL + ID(如果存在),用于避免重复下载\n const downloadKey = this._getDownloadKey(url, options.id);\n\n // 检查是否正在下载,避免重复下载同一资源\n if (this._downloadingUrls.has(downloadKey)) {\n return this._createErrorResult(\n `资源正在下载中,请勿重复下载: ${url}${options.id ? ` (ID: ${options.id})` : ''}`,\n 'VALIDATION'\n );\n }\n\n // 合并进度回调:优先使用传入的 onProgress,其次使用 options.onProgress\n const progressCallback = onProgress || options.onProgress;\n\n // 检查是否是 macOS App Store 环境(process.mas)\n // 在 MAS 环境下,强制显示保存对话框,且不使用临时文件\n const isMas = process.mas === true;\n if (isMas) {\n options.showSaveDialog = true;\n }\n\n // 先检查是否是 base64 data URL(用于确定默认扩展名和特殊处理)\n const base64Info = this._isBase64DataUrl(url);\n const defaultExt = base64Info.isBase64 && base64Info.ext ? base64Info.ext : undefined;\n\n // 确定输出路径:支持弹窗选择或自动解析\n let outputPath: string;\n let downloadDir: string;\n let downloadFileName: string;\n\n try {\n if (options.showSaveDialog) {\n // 显示保存对话框,让用户选择保存位置\n // 在 MAS 环境下,传递 isMas 标志以跳过权限检查\n const savePath = await this._showSaveDialog(url, options, defaultExt, isMas);\n if (!savePath) {\n return this._createErrorResult('用户取消了保存操作', 'USER_CANCEL');\n }\n outputPath = savePath;\n\n // 在 MAS 环境下,直接使用用户选择的路径,跳过多余的验证\n if (isMas) {\n // 直接提取目录和文件名,不进行额外的验证和清理\n // 信任系统返回的有效路径,避免权限问题\n downloadDir = path.dirname(outputPath);\n downloadFileName = path.basename(outputPath);\n } else {\n // 非 MAS 环境,进行正常的路径验证和处理\n // 提取下载目录和文件名\n downloadDir = path.dirname(outputPath);\n downloadFileName = path.basename(outputPath);\n // 清理文件名,确保不包含 query 参数和特殊字符\n downloadFileName = this._cleanFileName(downloadFileName);\n // 更新 outputPath 使用清理后的文件名\n outputPath = path.join(downloadDir, downloadFileName);\n }\n } else {\n // 自动解析输出路径\n outputPath = this._resolveOutputPath(url, options, defaultExt);\n\n // 验证 outputPath 是否有效\n if (!outputPath || typeof outputPath !== 'string' || outputPath.trim().length === 0) {\n return this._createErrorResult(\n '无法确定输出路径:路径解析结果无效',\n 'VALIDATION'\n );\n }\n\n // 准备输出路径:确保目录存在、检查文件是否已存在\n try {\n const pathCheckResult = await this._prepareOutputPath(outputPath, options);\n if (pathCheckResult) {\n // 文件已存在且不允许覆盖,直接返回已存在的文件信息\n return pathCheckResult;\n }\n } catch (error) {\n // 路径准备失败:可能是权限问题或文件系统错误\n const parsedError = this._parseError(error, url);\n return this._createErrorResult(\n `准备输出路径失败: ${parsedError.message}`,\n parsedError.type\n );\n }\n\n // 提取下载目录和文件名\n downloadDir = path.dirname(outputPath);\n downloadFileName = path.basename(outputPath);\n\n // 再次清理文件名,确保不包含 query 参数和特殊字符(防止文件系统错误)\n downloadFileName = this._cleanFileName(downloadFileName);\n\n // 验证下载目录是否可访问,如果不可用则使用系统下载目录作为备用\n if (!this._isDirectoryAccessible(downloadDir)) {\n try {\n downloadDir = this._getDownloadsPath();\n // 确保备用目录存在(如果不存在则创建)\n if (!fs.existsSync(downloadDir)) {\n fs.mkdirSync(downloadDir, { recursive: true });\n // 创建后清除缓存,强制重新检查\n this._directoryAccessCache.delete(downloadDir);\n }\n // 验证备用目录是否可访问\n if (!this._isDirectoryAccessible(downloadDir)) {\n return this._createErrorResult(\n `无法访问下载目录: ${downloadDir},请检查目录权限`,\n 'PERMISSION'\n );\n }\n } catch (error) {\n const parsedError = this._parseError(error, url);\n return this._createErrorResult(\n `无法创建或访问备用下载目录: ${parsedError.message}`,\n parsedError.type\n );\n }\n }\n\n // 更新 outputPath 使用清理后的文件名和验证后的目录\n outputPath = path.join(downloadDir, downloadFileName);\n }\n } catch (error) {\n // 路径解析失败:可能是文件系统错误或权限问题\n const parsedError = this._parseError(error, url);\n return this._createErrorResult(\n `确定输出路径失败: ${parsedError.message}`,\n parsedError.type\n );\n }\n\n // 如果是 base64 data URL,使用已确定的输出路径进行特殊处理(无需网络请求)\n if (base64Info.isBase64 && base64Info.ext && base64Info.mimeType && base64Info.data) {\n return await this._handleBase64Download(\n url,\n { ext: base64Info.ext, mimeType: base64Info.mimeType, data: base64Info.data },\n outputPath,\n options,\n progressCallback\n );\n }\n\n // 使用临时文件下载(.cache 后缀),避免下载中断时文件不完整\n // 下载完成后会将临时文件重命名为最终文件\n // 注意:在 macOS App Store 环境下(process.mas === true),不使用临时文件,直接下载到最终路径\n const useTempFile = !isMas;\n const tempFilePath = useTempFile\n ? path.join(downloadDir, downloadFileName + '.cache')\n : outputPath;\n const actualFileName = useTempFile\n ? downloadFileName + '.cache'\n : downloadFileName;\n\n return new Promise<DownloadResult>((resolve, reject) => {\n // 创建下载器实例(使用 node-downloader-helper)\n const dl = new DownloaderHelper(url, downloadDir, {\n fileName: actualFileName,\n retry: {\n maxRetries: options.retry?.maxRetries ?? DEFAULT_RETRY.maxRetries,\n delay: options.retry?.delay ?? DEFAULT_RETRY.delay\n },\n timeout: options.timeout ?? DEFAULT_TIMEOUT,\n override: true, // 临时文件总是覆盖,避免残留文件\n httpRequestOptions: {\n followRedirect: options.httpRequestOptions?.followRedirect ?? true,\n maxRedirects: options.httpRequestOptions?.maxRedirects ?? MAX_REDIRECTS,\n headers: options.httpRequestOptions?.headers,\n // 设置 socket 超时时间(30秒),用于更快检测网络中断\n // 如果 socket 在 30 秒内没有数据传输,会触发超时\n timeout: 30000\n }\n });\n\n // 记录正在下载的 URL(使用 URL + ID 作为 key),用于避免重复下载\n this._downloadingUrls.set(downloadKey, dl);\n\n // 进度停滞检测:用于检测网络中断\n let lastProgressTime = Date.now();\n let lastDownloaded = 0;\n let progressStallTimer: NodeJS.Timeout | null = null;\n const PROGRESS_STALL_TIMEOUT = 60000; // 60秒没有进度更新则认为网络中断\n\n // 进度回调节流:避免频繁触发回调(每100ms最多触发一次)\n let lastProgressCallbackTime = 0;\n const PROGRESS_THROTTLE_MS = 100;\n let pendingProgress: Parameters<NonNullable<typeof progressCallback>>[0] | null = null;\n let progressThrottleTimer: NodeJS.Timeout | null = null;\n\n // 统一的错误处理函数(需要在 resetStallTimer 之前定义)\n const handleError = (error: Error | unknown, isUserCancel: boolean = false) => {\n // 清理所有定时器\n if (progressStallTimer) {\n clearTimeout(progressStallTimer);\n progressStallTimer = null;\n }\n if (progressThrottleTimer) {\n clearTimeout(progressThrottleTimer);\n progressThrottleTimer = null;\n }\n // 执行最后一次进度回调(如果有待发送的数据)\n if (pendingProgress) {\n flushProgressCallback();\n }\n // 从下载列表中移除\n this._downloadingUrls.delete(downloadKey);\n\n // 清理临时文件(忽略删除失败,避免影响错误处理)\n // 注意:在 macOS App Store 环境下不使用临时文件,无需清理\n if (useTempFile) {\n fs.promises.unlink(tempFilePath).catch(() => {\n // 临时文件删除失败不影响错误处理流程\n });\n }\n\n // 解析错误信息\n const parsedError = this._parseError(error, url);\n const errorType = isUserCancel ? 'USER_CANCEL' : parsedError.type;\n const errorMessage = isUserCancel\n ? `下载已取消: ${url}`\n : parsedError.message;\n\n // 创建错误对象(包含错误类型信息)\n const errorObj = new Error(errorMessage);\n // 将错误类型附加到错误对象上,供 catch 处理时使用\n (errorObj as any).errorType = errorType;\n\n // 调用错误回调\n if (options.onError) {\n options.onError(errorObj);\n }\n\n // reject 应该传入 Error 对象,而不是 DownloadResult\n reject(errorObj);\n };\n\n // 清理进度停滞检测定时器\n const clearStallTimer = () => {\n if (progressStallTimer) {\n clearTimeout(progressStallTimer);\n progressStallTimer = null;\n }\n };\n\n // 重置进度停滞检测定时器\n const resetStallTimer = () => {\n clearStallTimer();\n progressStallTimer = setTimeout(() => {\n const now = Date.now();\n const timeSinceLastProgress = now - lastProgressTime;\n // 如果超过停滞时间阈值,认为网络已中断\n if (timeSinceLastProgress >= PROGRESS_STALL_TIMEOUT) {\n const error = new Error(`网络连接中断:下载进度在 ${Math.round(timeSinceLastProgress / 1000)} 秒内没有更新`);\n (error as any).code = 'ENETWORKSTALL';\n handleError(error, false);\n }\n }, PROGRESS_STALL_TIMEOUT);\n };\n\n // 节流进度回调执行\n const flushProgressCallback = () => {\n if (pendingProgress && progressCallback) {\n try {\n progressCallback(pendingProgress);\n pendingProgress = null;\n } catch (error) {\n // 进度回调失败不影响下载,只记录日志\n console.error(`[Downloader] 进度回调执行失败 (${url}):`, error);\n }\n }\n progressThrottleTimer = null;\n };\n\n // 监听下载进度事件\n dl.on('progress', (stats) => {\n const currentDownloaded = stats.downloaded || 0;\n const currentTime = Date.now();\n\n // 如果下载量有增加,更新最后进度时间和下载量,并重置停滞检测定时器\n if (currentDownloaded > lastDownloaded) {\n lastDownloaded = currentDownloaded;\n lastProgressTime = currentTime;\n // 重置停滞检测定时器(因为下载有进展)\n resetStallTimer();\n }\n\n // 节流进度回调:更新待发送的进度数据\n if (progressCallback) {\n pendingProgress = {\n url,\n downloaded: currentDownloaded,\n total: stats.total || 0,\n percentage: stats.progress !== undefined ? Math.round(stats.progress) : -1,\n speed: stats.speed || 0\n };\n\n // 如果距离上次回调超过节流时间,立即执行\n const timeSinceLastCallback = currentTime - lastProgressCallbackTime;\n if (timeSinceLastCallback >= PROGRESS_THROTTLE_MS) {\n flushProgressCallback();\n lastProgressCallbackTime = currentTime;\n } else if (!progressThrottleTimer) {\n // 否则设置定时器延迟执行\n progressThrottleTimer = setTimeout(() => {\n flushProgressCallback();\n lastProgressCallbackTime = Date.now();\n }, PROGRESS_THROTTLE_MS - timeSinceLastCallback);\n }\n }\n });\n\n // 启动进度停滞检测\n resetStallTimer();\n\n // 监听下载完成事件\n dl.on('end', () => {\n clearStallTimer();\n // 清理进度节流定时器\n if (progressThrottleTimer) {\n clearTimeout(progressThrottleTimer);\n progressThrottleTimer = null;\n }\n // 执行最后一次进度回调(确保100%进度被发送)\n if (pendingProgress) {\n flushProgressCallback();\n }\n // 从下载列表中移除\n this._downloadingUrls.delete(downloadKey);\n\n // 在 macOS App Store 环境下,不使用临时文件,直接使用最终路径\n if (!useTempFile) {\n // 直接获取文件信息并返回成功结果\n fs.promises.stat(outputPath).then(async (stats) => {\n try {\n const result = this._createSuccessResult(outputPath, stats.size);\n\n // 调用完成回调\n if (options.onComplete) {\n options.onComplete(outputPath);\n }\n\n resolve(result);\n } catch (error) {\n // 获取文件信息失败\n const parsedError = this._parseError(error, url);\n const errorObj = new Error(`下载完成但无法获取文件信息: ${parsedError.message}`);\n // 将错误类型附加到错误对象上\n (errorObj as any).errorType = parsedError.type;\n reject(errorObj);\n }\n }).catch((statErr) => {\n // 获取文件信息失败\n const parsedError = this._parseError(statErr, url);\n const error = new Error(`下载完成但无法获取文件信息: ${parsedError.message}`);\n\n // 调用错误回调\n if (options.onError) {\n options.onError(error);\n }\n\n // reject 应该传入 Error 对象,而不是 DownloadResult\n // 将错误类型附加到错误对象上\n (error as any).errorType = parsedError.type;\n reject(error);\n });\n } else {\n // 将临时文件重命名为最终文件(使用异步操作,避免阻塞)\n fs.promises.rename(tempFilePath, outputPath).then(async () => {\n try {\n // 获取文件大小并返回成功结果(使用异步操作)\n const stats = await fs.promises.stat(outputPath);\n const result = this._createSuccessResult(outputPath, stats.size);\n\n // 调用完成回调\n if (options.onComplete) {\n options.onComplete(outputPath);\n }\n\n resolve(result);\n } catch (error) {\n // 获取文件信息失败\n const parsedError = this._parseError(error, url);\n const errorObj = new Error(`下载完成但无法获取文件信息: ${parsedError.message}`);\n // 将错误类型附加到错误对象上\n (errorObj as any).errorType = parsedError.type;\n reject(errorObj);\n }\n }).catch((renameErr) => {\n // 文件重命名失败:可能是权限问题或文件被占用\n const parsedError = this._parseError(renameErr, url);\n const error = new Error(\n `文件重命名失败: 无法将临时文件 \"${tempFilePath}\" 重命名为 \"${outputPath}\"。${parsedError.message}`\n );\n\n // 调用错误回调\n if (options.onError) {\n options.onError(error);\n }\n\n // reject 应该传入 Error 对象,而不是 DownloadResult\n // 将错误类型附加到错误对象上\n (error as any).errorType = parsedError.type;\n reject(error);\n });\n }\n });\n\n // 监听超时事件(socket 超时,通常表示网络中断)\n dl.on('timeout', () => {\n const error = new Error(`网络连接超时:下载在指定时间内没有数据传输`);\n (error as any).code = 'ETIMEDOUT';\n handleError(error, false);\n });\n\n // 监听下载错误事件(网络错误、服务器错误等)\n dl.on('error', (err) => {\n // 检查是否是超时错误\n const errorStats = err as any;\n let error: Error;\n if (errorStats && typeof errorStats === 'object') {\n // node-downloader-helper 的 error 事件传递的是 ErrorStats 对象\n error = new Error(errorStats.message || `下载失败: ${url}`);\n if (errorStats.status) {\n (error as any).status = errorStats.status;\n }\n } else if (err instanceof Error) {\n error = err;\n } else {\n error = new Error(`下载失败: ${url}`);\n }\n handleError(error, false);\n });\n\n // 监听下载停止事件(用户取消或手动停止)\n dl.on('stop', () => {\n const error = new Error(`下载已停止: ${url}`);\n handleError(error, true);\n });\n\n // 开始下载\n dl.start().catch((err) => {\n // 启动下载失败:可能是 URL 无效、网络不可达等\n const error = err instanceof Error ? err : new Error(`启动下载失败: ${url}`);\n handleError(error, false);\n });\n }).catch((error): DownloadResult => {\n // Promise 异常捕获:返回格式化的错误结果\n // 如果错误对象已经包含 errorType,直接使用;否则解析错误\n if (error instanceof Error && (error as any).errorType) {\n return this._createErrorResult(error.message, (error as any).errorType);\n }\n const parsedError = this._parseError(error, url);\n return this._createErrorResult(parsedError.message, parsedError.type);\n });\n }\n\n /**\n * 生成下载任务的唯一标识\n * @param url 下载 URL\n * @param id 可选的任务 ID\n * @returns 唯一标识\n */\n private _getDownloadKey(url: string, id?: string): string {\n return id ? `${url}::${id}` : url;\n }\n\n /**\n * 取消下载\n * @param url 要取消的 URL\n * @param id 可选的任务 ID\n * @returns 是否成功取消\n */\n public cancel(url: string, id?: string): boolean {\n const downloadKey = this._getDownloadKey(url, id);\n const dl = this._downloadingUrls.get(downloadKey);\n if (dl) {\n dl.stop();\n this._downloadingUrls.delete(downloadKey);\n return true;\n }\n return false;\n }\n\n /**\n * 取消所有正在进行的下载\n */\n public cancelAll(): void {\n for (const [url, dl] of this._downloadingUrls.entries()) {\n dl.stop();\n }\n this._downloadingUrls.clear();\n }\n\n /**\n * 检查指定 URL 是否正在下载\n * @param url 要检查的 URL\n * @param id 可选的任务 ID\n * @returns 是否正在下载\n */\n public isDownloading(url: string, id?: string): boolean {\n const downloadKey = this._getDownloadKey(url, id);\n return this._downloadingUrls.has(downloadKey);\n }\n\n /**\n * 获取正在下载的任务列表\n * @returns 正在下载的任务信息数组,包含 URL 和 ID(如果有)\n */\n public getDownloadingUrls(): Array<{ url: string; id?: string }> {\n return Array.from(this._downloadingUrls.keys()).map(key => {\n const parts = key.split('::');\n if (parts.length === 2 && parts[0] && parts[1]) {\n return { url: parts[0], id: parts[1] };\n }\n return { url: key };\n });\n }\n\n /**\n * 获取文件名(统一处理所有来源)\n * @param url 下载 URL\n * @param options 下载选项\n * @param defaultExt 默认扩展名(可选,用于 base64 URL)\n * @returns 清理后的文件名\n */\n private _getFileName(url: string, options: DownloadOptions, defaultExt?: string): string {\n // 优先级:defaultPath > fileName > defaultExt > URL提取\n if (options.defaultPath) {\n return this._cleanFileName(path.basename(options.defaultPath));\n }\n\n if (options.fileName) {\n return this._cleanFileName(options.fileName);\n }\n\n if (defaultExt) {\n return this._cleanFileName(`${DEFAULT_FILENAME}.${defaultExt}`);\n }\n // 从 URL 中提取文件名(_extractFileNameFromUrl 已经包含清理逻辑)\n return this._extractFileNameFromUrl(url);\n }\n\n /**\n * 获取下载目录路径(带缓存)\n */\n private _getDownloadsPath(): string {\n if (this._cachedDownloadsPath === null) {\n this._cachedDownloadsPath = app.getPath('downloads');\n }\n return this._cachedDownloadsPath;\n }\n\n /**\n * 检查目录是否存在且有访问权限(带缓存优化)\n *\n * 检查项:\n * 1. 路径是否有效(非空字符串)\n * 2. 目录是否存在\n * 3. 路径是否为目录(而非文件)\n * 4. 是否有读写权限\n *\n * @param dirPath 目录路径\n * @returns 如果目录存在且有读写权限则返回 true,否则返回 false\n */\n private _isDirectoryAccessible(dirPath: string): boolean {\n // 参数验证:检查路径是否有效\n if (!dirPath || typeof dirPath !== 'string') {\n return false;\n }\n\n // 检查缓存\n const cached = this._directoryAccessCache.get(dirPath);\n const now = Date.now();\n if (cached && (now - cached.timestamp) < this._cacheTTL) {\n return cached.accessible;\n }\n\n let accessible = false;\n try {\n // 优化:使用 statSync 同时检查存在性和类型,然后检查权限\n // 这样可以减少文件系统调用次数\n const stats = fs.statSync(dirPath);\n if (stats.isDirectory()) {\n // 检查是否有读写权限(R_OK: 读权限, W_OK: 写权限)\n fs.accessSync(dirPath, fs.constants.R_OK | fs.constants.W_OK);\n accessible = true;\n }\n } catch {\n // 如果任何检查失败(不存在、不是目录、无权限等),返回 false\n accessible = false;\n }\n\n // 更新缓存\n this._directoryAccessCache.set(dirPath, { accessible, timestamp: now });\n\n // 清理过期缓存(简单策略:当缓存超过100个条目时清理)\n if (this._directoryAccessCache.size > 100) {\n for (const [key, value] of this._directoryAccessCache.entries()) {\n if ((now - value.timestamp) >= this._cacheTTL) {\n this._directoryAccessCache.delete(key);\n }\n }\n }\n\n return accessible;\n }\n\n /**\n * 显示保存对话框\n *\n * 功能说明:\n * 1. 确定默认保存目录(优先级:defaultPath > outputDir > _lastSelectedDir > 系统下载目录)\n * 2. 验证并更新 _lastSelectedDir(如果不可访问则重置)\n * 3. 生成唯一文件名(如果文件已存在,添加数字后缀)\n * 4. 显示 Electron 保存对话框\n * 5. 记录用户选择的目录(用于下次默认使用)\n *\n * @param url 下载 URL(用于提取默认文件名)\n * @param options 下载选项(包含 defaultPath、outputDir 等)\n * @param defaultExt 默认扩展名(可选,用于 base64 URL)\n * @param isMas 是否是 macOS App Store 环境(可选,用于跳过权限检查)\n * @returns Promise<string | null> 用户选择的保存路径,如果取消则返回 null\n */\n private async _showSaveDialog(url: string, options: DownloadOptions, defaultExt?: string, isMas: boolean = false): Promise<string | null> {\n // 获取并清理默认文件名\n let defaultFileName = this._getFileName(url, options, defaultExt);\n\n // 获取默认目录\n let defaultDir: string | undefined;\n if (options.defaultPath) {\n defaultDir = path.dirname(options.defaultPath);\n } else if (options.outputDir) {\n defaultDir = path.resolve(options.outputDir);\n } else {\n // 如果没有指定目录,使用上一次选择的文件夹,如果没有则使用下载目录\n // 在 MAS 环境下,跳过权限检查,直接使用缓存的目录或下载目录\n if (isMas) {\n defaultDir = this._lastSelectedDir || this._getDownloadsPath();\n } else {\n // 检查 _lastSelectedDir 是否可用,如果不可用则重置为下载目录\n if (this._lastSelectedDir && !this._isDirectoryAccessible(this._lastSelectedDir)) {\n const downloadsPath = this._getDownloadsPath();\n // 验证下载目录是否可用,如果可用则使用,否则设为 null\n this._lastSelectedDir = this._isDirectoryAccessible(downloadsPath) ? downloadsPath : null;\n }\n defaultDir = this._lastSelectedDir || this._getDownloadsPath();\n }\n }\n\n // 如果指定了目录,检查文件是否存在并生成唯一文件名\n // 在 MAS 环境下,跳过文件系统访问,直接使用默认文件名\n if (defaultDir && !isMas) {\n defaultFileName = this._generateUniqueFileName(defaultDir, defaultFileName);\n }\n\n // 构建默认路径\n const defaultPath = defaultDir ? path.join(defaultDir, defaultFileName) : defaultFileName;\n\n // 获取当前活动的 BrowserWindow\n const focusedWindow = BrowserWindow.getFocusedWindow();\n const allWindows = BrowserWindow.getAllWindows();\n const parentWindow = focusedWindow || (allWindows.length > 0 ? allWindows[0] : undefined);\n\n // 显示保存对话框\n const dialogOptions = {\n defaultPath,\n title: '保存文件',\n buttonLabel: '保存',\n filters: [\n // 尝试从文件名推断文件类型\n ...(path.extname(defaultFileName) ? [{\n name: '所有文件',\n extensions: ['*']\n }] : [])\n ]\n };\n\n // 根据是否有父窗口调用不同的方法\n const result = parentWindow\n ? await dialog.showSaveDialog(parentWindow, dialogOptions)\n : await dialog.showSaveDialog(dialogOptions);\n\n if (result.canceled || !result.filePath) {\n return null;\n }\n\n // 记录用户选择的文件夹路径,用于下次默认使用\n // 在 MAS 环境下,直接保存用户选择的目录,不进行权限检查\n const selectedDir = path.dirname(result.filePath);\n if (selectedDir) {\n if (isMas) {\n // MAS 环境下,直接保存用户选择的目录,信任系统返回的路径\n this._lastSelectedDir = selectedDir;\n } else {\n // 验证选择的目录是否可访问,只有可访问时才保存\n if (this._isDirectoryAccessible(selectedDir)) {\n this._lastSelectedDir = selectedDir;\n } else {\n // 如果选择的目录不可访问,尝试使用下载目录\n const downloadsPath = this._getDownloadsPath();\n this._lastSelectedDir = this._isDirectoryAccessible(downloadsPath) ? downloadsPath : null;\n }\n }\n }\n\n return result.filePath;\n }\n\n /**\n * 检测并处理 base64 data URL\n * @param url 资源URL\n * @returns 如果是 base64 URL,返回 true、文件扩展名、MIME 类型和数据;否则返回 false\n */\n private _isBase64DataUrl(url: string): { isBase64: boolean, ext?: string, mimeType?: string, data?: string } {\n if (!url.startsWith('data:')) {\n return { isBase64: false };\n }\n\n try {\n // 解析 data URL 格式:data:[<mediatype>][;base64],<data>\n const commaIndex = url.indexOf(',');\n if (commaIndex === -1) {\n return { isBase64: false };\n }\n\n const header = url.substring(0, commaIndex);\n const data = url.substring(commaIndex + 1);\n\n // 检查是否包含 base64 标识\n if (!header.includes('base64')) {\n return { isBase64: false };\n }\n\n // 从 mediatype 中提取文件扩展名和 MIME 类型\n // 例如:data:image/png;base64 -> png, image/png\n // 例如:data:image/jpeg;base64 -> jpeg, image/jpeg\n let ext = DEFAULT_EXT.replace(/^\\./, '');\n let mimeType = DEFAULT_MIME_TYPE;\n const mimeMatch = header.match(/data:([^;]+)/);\n if (mimeMatch && mimeMatch[1]) {\n mimeType = mimeMatch[1];\n // 使用 mime-types 包从 MIME 类型获取扩展名\n const extension = mime.extension(mimeType);\n if (extension) {\n ext = extension;\n }\n }\n\n return { isBase64: true, ext, mimeType, data };\n } catch (error) {\n return { isBase64: false };\n }\n }\n\n\n /**\n * 准备输出路径\n *\n * 主要功能:\n * 1. 确保输出目录存在(不存在则创建)\n * 2. 检查文件是否已存在\n * 3. 如果文件已存在且不允许覆盖,返回已存在的文件信息\n *\n * @param outputPath 输出文件路径\n * @param options 下载选项(包含 override 选项)\n * @returns 如果文件已存在且不允许覆盖,返回已存在的文件信息;否则返回 null\n * @throws 如果目录创建失败或权限不足,抛出错误\n */\n private async _prepareOutputPath(\n outputPath: string,\n options: DownloadOptions\n ): Promise<DownloadResult | null> {\n // 参数验证:确保 outputPath 是有效的非空字符串\n if (!outputPath || typeof outputPath !== 'string' || outputPath.trim().length === 0) {\n throw new Error(`输出路径无效: ${outputPath}`);\n }\n\n // 规范化路径(解析相对路径、处理 .. 等)\n const normalizedPath = path.resolve(outputPath);\n\n // 验证规范化后的路径是否有效\n if (!normalizedPath || normalizedPath.trim().length === 0) {\n throw new Error(`无法解析输出路径: ${outputPath}`);\n }\n\n const downloadDir = path.dirname(normalizedPath);\n\n // 使用规范化后的路径\n outputPath = normalizedPath;\n\n // 确保目录存在:如果不存在则创建(递归创建父目录)\n if (!fs.existsSync(downloadDir)) {\n try {\n fs.mkdirSync(downloadDir, { recursive: true });\n } catch (error) {\n // 目录创建失败:可能是权限问题或路径无效\n const parsedError = this._parseError(error);\n throw new Error(`无法创建输出目录 \"${downloadDir}\": ${parsedError.message}`);\n }\n }\n\n // 验证目录是否可访问\n if (!this._isDirectoryAccessible(downloadDir)) {\n throw new Error(`无法访问输出目录 \"${downloadDir}\",请检查目录权限`);\n }\n\n // 如果文件已存在且不允许覆盖,直接返回已存在的文件信息\n if (fs.existsSync(outputPath) && !(options.override ?? true)) {\n try {\n const stats = fs.statSync(outputPath);\n return this._createSuccessResult(outputPath, stats.size);\n } catch (error) {\n // 获取文件信息失败:可能是权限问题\n const parsedError = this._parseError(error);\n throw new Error(`无法访问已存在的文件 \"${outputPath}\": ${parsedError.message}`);\n }\n }\n\n return null;\n }\n\n /**\n * 创建成功结果\n * @param filePath 文件路径\n * @param size 文件大小(字节)\n * @returns DownloadResult 成功结果对象\n */\n private _createSuccessResult(filePath: string, size: number): DownloadResult {\n return {\n success: true,\n filePath,\n size\n };\n }\n\n /**\n * 创建错误结果\n * @param error 错误信息\n * @param errorType 错误类型(可选,用于区分不同类型的错误)\n * @returns DownloadResult 错误结果对象\n */\n private _createErrorResult(error: string, errorType?: DownloadResult['errorType']): DownloadResult {\n return {\n success: false,\n error,\n errorType: errorType || 'UNKNOWN'\n };\n }\n\n /**\n * 从错误对象中提取错误类型和详细信息(性能优化版本)\n * @param error 错误对象\n * @param url 下载的 URL(用于上下文信息)\n * @returns 包含错误类型和详细错误信息的对象\n */\n private _parseError(error: unknown, url?: string): { type: DownloadResult['errorType']; message: string } {\n if (error instanceof Error) {\n // 优化:只转换一次为小写,避免重复转换\n const errorMessage = error.message.toLowerCase();\n const errorCode = ((error as any).code || '').toLowerCase();\n const urlSuffix = url ? ` (URL: ${url})` : '';\n\n // 优化:优先检查错误代码(更可靠且更快)\n if (errorCode) {\n if (errorCode === 'enetworkstall' || errorCode === 'etimedout' ||\n errorCode === 'econnrefused' || errorCode === 'enotfound') {\n return {\n type: 'NETWORK',\n message: `网络错误: ${error.message}${urlSuffix}`\n };\n }\n if (errorCode === 'eacces' || errorCode === 'eperm') {\n return {\n type: 'PERMISSION',\n message: `权限错误: ${error.message}${urlSuffix}`\n };\n }\n if (errorCode === 'enoent') {\n return {\n type: 'FILE_SYSTEM',\n message: `文件系统错误: ${error.message}${urlSuffix}`\n };\n }\n }\n\n // 检查错误消息(作为后备)\n // 网络相关错误\n if (errorMessage.includes('network') || errorMessage.includes('timeout') ||\n errorMessage.includes('econnrefused') || errorMessage.includes('enotfound') ||\n errorMessage.includes('etimedout') || errorMessage.includes('连接中断') ||\n errorMessage.includes('连接超时')) {\n return {\n type: 'NETWORK',\n message: `网络错误: ${error.message}${urlSuffix}`\n };\n }\n\n // 文件系统相关错误\n if (errorMessage.includes('enoent') || errorMessage.includes('eacces') ||\n errorMessage.includes('eperm') || errorMessage.includes('file') ||\n errorMessage.includes('directory') || errorMessage.includes('path')) {\n if (errorMessage.includes('permission') || errorMessage.includes('eacces') ||\n errorMessage.includes('eperm')) {\n return {\n type: 'PERMISSION',\n message: `权限错误: ${error.message}${urlSuffix}`\n };\n }\n return {\n type: 'FILE_SYSTEM',\n message: `文件系统错误: ${error.message}${urlSuffix}`\n };\n }\n\n // 验证相关错误\n if (errorMessage.includes('invalid') || errorMessage.includes('validation') ||\n errorMessage.includes('格式') || errorMessage.includes('无效')) {\n return {\n type: 'VALIDATION',\n message: `验证错误: ${error.message}${urlSuffix}`\n };\n }\n\n // 用户取消\n if (errorMessage.includes('cancel') || errorMessage.includes('取消')) {\n return {\n type: 'USER_CANCEL',\n message: error.message\n };\n }\n\n // 其他错误\n return {\n type: 'UNKNOWN',\n message: `未知错误: ${error.message}${urlSuffix}`\n };\n }\n\n // 非 Error 对象\n const urlSuffix = url ? ` (URL: ${url})` : '';\n return {\n type: 'UNKNOWN',\n message: `未知错误: ${String(error)}${urlSuffix}`\n };\n }\n\n /**\n * 处理 base64 data URL 的下载\n *\n * base64 data URL 格式:data:[<mediatype>][;base64],<data>\n * 例如:data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...\n *\n * 处理流程:\n * 1. 验证 base64 数据有效性\n * 2. 触发进度回调(模拟 100% 完成)\n * 3. 解码并保存 base64 数据到文件\n * 4. 返回成功结果\n *\n * @param url base64 data URL\n * @param base64Info base64 信息(扩展名、MIME 类型、数据)\n * @param outputPath 已确定的输出路径\n * @param options 下载选项\n * @param progressCallback 进度回调(可选)\n * @returns Promise<DownloadResult> 下载结果\n */\n private async _handleBase64Download(\n url: string,\n base64Info: { ext: string; mimeType: string; data: string },\n outputPath: string,\n options: DownloadOptions,\n progressCallback?: DownloadProgressCallback\n ): Promise<DownloadResult> {\n // 验证 base64 数据有效性\n if (!base64Info.data || !base64Info.ext) {\n return this._createErrorResult(\n `无效的 base64 data URL: 缺少数据或扩展名 (URL: ${url.substring(0, 50)}...)`,\n 'VALIDATION'\n );\n }\n\n try {\n // 优化:直接解码 base64 数据,避免重复创建 Buffer\n const buffer = Buffer.from(base64Info.data, 'base64');\n const dataLength = buffer.length;\n\n // 模拟进度回调(base64 数据通常很小,立即完成)\n if (progressCallback) {\n progressCallback({\n url,\n downloaded: dataLength,\n total: dataLength,\n percentage: 100,\n speed: 0\n });\n }\n\n // 保存 base64 数据到文件(直接使用已解码的 buffer)\n // 使用 Buffer.from 创建新的 Buffer 实例,确保类型兼容\n await fs.promises.writeFile(outputPath, Buffer.from(buffer as any) as Uint8Array);\n\n // 获取文件大小并返回成功结果\n const stats = fs.statSync(outputPath);\n const result = this._createSuccessResult(outputPath, stats.size);\n\n // 调用完成回调\n if (options.onComplete) {\n options.onComplete(outputPath);\n }\n\n return result;\n } catch (error) {\n // base64 文件保存失败:可能是文件系统错误或权限问题\n const parsedError = this._parseError(error, url);\n const err = error instanceof Error ? error : new Error(`保存 base64 文件失败: ${parsedError.message}`);\n\n // 调用错误回调\n if (options.onError) {\n options.onError(err);\n }\n\n return this._createErrorResult(\n `保存 base64 文件失败: ${parsedError.message}`,\n parsedError.type\n );\n }\n }\n\n /**\n * 生成唯一的文件名(如果文件已存在,添加数字后缀)\n *\n * 命名规则:\n * - 如果文件不存在,返回原始文件名\n * - 如果文件已存在,添加数字后缀:filename(1).ext, filename(2).ext, ...\n * - 最多尝试 MAX_FILENAME_COUNTER 次,防止无限循环\n *\n * 性能优化:\n * - 使用二分查找优化查找范围(如果文件很多)\n * - 限制最大检查次数\n *\n * 示例:\n * - \"test.pdf\" -> \"test.pdf\" (如果不存在)\n * - \"test.pdf\" -> \"test(1).pdf\" (如果 test.pdf 已存在)\n * - \"test(1).pdf\" -> \"test(2).pdf\" (如果 test(1).pdf 也已存在)\n *\n * @param dir 目录路径\n * @param fileName 原始文件名(包含扩展名)\n * @returns 唯一的文件名(如果原文件不存在则返回原文件名,否则返回带数字后缀的文件名)\n */\n private _generateUniqueFileName(dir: string, fileName: string): string {\n // 分离文件名和扩展名\n const ext = path.extname(fileName);\n const nameWithoutExt = path.basename(fileName, ext);\n\n // 检查原始文件名是否存在\n const originalPath = path.join(dir, fileName);\n if (!fs.existsSync(originalPath)) {\n return fileName;\n }\n\n // 如果存在,尝试添加数字后缀\n // 优化:使用线性查找,但限制最大检查次数\n let counter = 1;\n let newFileName = `${nameWithoutExt}(${counter})${ext}`;\n let newPath: string;\n\n // 快速查找:先尝试常见的数字(1-10)\n while (counter <= 10 && counter < MAX_FILENAME_COUNTER) {\n newFileName = `${nameWithoutExt}(${counter})${ext}`;\n newPath = path.join(dir, newFileName);\n if (!fs.existsSync(newPath)) {\n return newFileName;\n }\n counter++;\n }\n\n // 如果前10个都被占用,继续查找(但限制最大次数)\n while (counter < MAX_FILENAME_COUNTER) {\n newFileName = `${nameWithoutExt}(${counter})${ext}`;\n newPath = path.join(dir, newFileName);\n if (!fs.existsSync(newPath)) {\n return newFileName;\n }\n counter++;\n }\n\n // 如果达到最大次数,返回最后一个尝试的文件名\n return newFileName;\n }\n\n /**\n * 清理文件名,移除 query 参数和特殊字符\n * @param fileName 原始文件名\n * @returns 清理后的文件名\n */\n private _cleanFileName(fileName: string): string {\n if (!fileName || fileName.length === 0) {\n return DEFAULT_FILENAME;\n }\n\n // 移除 query 参数(如果文件名中包含 ?)\n const queryIndex = fileName.indexOf('?');\n if (queryIndex !== -1) {\n fileName = fileName.substring(0, queryIndex);\n }\n\n // 解码 URL 编码的字符(如 %20 -> 空格)\n try {\n fileName = decodeURIComponent(fileName);\n } catch {\n // 如果解码失败,使用原始文件名\n }\n\n // 清理文件名中的特殊字符(文件系统不允许的字符)\n // 移除或替换:/ \\ : * ? \" < > |\n fileName = fileName.replace(/[/\\\\:*?\"<>|]/g, '_');\n\n // 移除前后空格和点号(Windows 不允许)\n fileName = fileName.trim().replace(/^\\.+|\\.+$/g, '');\n\n // 如果清理后文件名为空,使用默认文件名\n if (!fileName || fileName.length === 0) {\n fileName = DEFAULT_FILENAME;\n }\n\n return fileName;\n }\n\n /**\n * 从 URL 中提取文件名\n * @param url 下载 URL\n * @returns 文件名(已清理特殊字符,不包含query参数)\n */\n private _extractFileNameFromUrl(url: string): string {\n if (!url || typeof url !== 'string' || url.trim().length === 0) {\n return `${DEFAULT_FILENAME}${DEFAULT_EXT}`;\n }\n\n try {\n // 确保 URL 是完整的(如果缺少协议,添加 https://)\n let normalizedUrl = url.trim();\n if (!normalizedUrl.match(/^https?:\\/\\//i)) {\n normalizedUrl = `https://${normalizedUrl}`;\n }\n\n const urlObj = new URL(normalizedUrl);\n\n // 验证 pathname 是否有效\n if (!urlObj.pathname || urlObj.pathname === '/' || urlObj.pathname.length === 0) {\n return `${DEFAULT_FILENAME}${DEFAULT_EXT}`;\n }\n\n // 只使用 pathname 部分,确保不包含 query 参数\n let fileName = path.basename(urlObj.pathname);\n\n // 如果 pathname 以 / 结尾或 basename 为空,尝试从路径中提取\n if (!fileName || fileName === '/' || fileName.length === 0) {\n // 移除首尾的斜杠,然后取最后一部分\n const pathParts = urlObj.pathname.replace(/^\\/+|\\/+$/g, '').split('/');\n fileName = pathParts[pathParts.length - 1] || DEFAULT_FILENAME;\n }\n\n // 如果还是没有有效的文件名,使用默认值\n if (!fileName || fileName === '/' || fileName.length === 0) {\n return `${DEFAULT_FILENAME}${DEFAULT_EXT}`;\n }\n\n // 解码 URL 编码的字符(如 %20 -> 空格)\n try {\n fileName = decodeURIComponent(fileName);\n } catch {\n // 如果解码失败,使用原始文件名\n }\n\n // 清理文件名中的特殊字符(文件系统不允许的字符)\n // 移除或替换:/ \\ : * ? \" < > |\n fileName = fileName.replace(/[/\\\\:*?\"<>|]/g, '_');\n\n // 移除前后空格和点号(Windows 不允许)\n fileName = fileName.trim().replace(/^\\.+|\\.+$/g, '');\n\n // 如果清理后文件名为空,使用默认文件名\n if (!fileName || fileName.length === 0) {\n fileName = DEFAULT_FILENAME;\n }\n\n // 确保有扩展名\n return path.extname(fileName) ? fileName : `${fileName}${DEFAULT_EXT}`;\n } catch (error) {\n // URL 解析失败,尝试手动提取\n try {\n // 移除 query 参数和 hash\n const urlWithoutQuery = url.split('?')[0];\n const cleanUrl = urlWithoutQuery ? urlWithoutQuery.split('#')[0] : url;\n if (cleanUrl) {\n // 尝试提取最后一个路径段\n const match = cleanUrl.match(/\\/([^\\/]+)$/);\n if (match && match[1]) {\n let fileName = match[1];\n // 清理文件名\n fileName = fileName.replace(/[/\\\\:*?\"<>|]/g, '_');\n fileName = fileName.trim().replace(/^\\.+|\\.+$/g, '');\n if (fileName && fileName.length > 0) {\n return path.extname(fileName) ? fileName : `${fileName}${DEFAULT_EXT}`;\n }\n }\n }\n } catch {\n // 忽略手动提取的错误\n }\n return `${DEFAULT_FILENAME}${DEFAULT_EXT}`;\n }\n }\n\n /**\n * 解析输出路径\n * @param url 下载 URL\n * @param options 下载选项\n * @param defaultExt 默认扩展名(可选,用于 base64 URL)\n * @returns 输出文件路径\n * @throws 如果无法解析有效路径,抛出错误\n */\n private _resolveOutputPath(url: string, options: DownloadOptions, defaultExt?: string): string {\n // 如果提供了完整路径,直接使用(但需要清理文件名部分)\n if (options.outputPath) {\n // 验证 outputPath 是否有效\n if (typeof options.outputPath !== 'string' || options.outputPath.trim().length === 0) {\n throw new Error(`无效的输出路径: ${options.outputPath}`);\n }\n\n try {\n const dir = path.dirname(options.outputPath);\n const fileName = this._cleanFileName(path.basename(options.outputPath));\n\n // 验证文件名是否有效\n if (!fileName || fileName.trim().length === 0) {\n throw new Error(`无法从路径中提取有效文件名: ${options.outputPath}`);\n }\n\n const resolvedPath = path.resolve(path.join(dir, fileName));\n\n // 验证解析后的路径是否有效\n if (!resolvedPath || resolvedPath.trim().length === 0) {\n throw new Error(`路径解析失败: ${options.outputPath}`);\n }\n\n return resolvedPath;\n } catch (error) {\n if (error instanceof Error) {\n throw error;\n }\n throw new Error(`解析输出路径失败: ${String(error)}`);\n }\n }\n\n // 确定输出目录:优先使用指定的目录,否则使用 Electron 的下载目录\n let outputDir: string;\n try {\n if (options.outputDir) {\n if (typeof options.outputDir !== 'string' || options.outputDir.trim().length === 0) {\n throw new Error(`无效的输出目录: ${options.outputDir}`);\n }\n outputDir = path.resolve(options.outputDir);\n } else {\n outputDir = this._getDownloadsPath();\n }\n\n // 验证输出目录是否有效\n if (!outputDir || outputDir.trim().length === 0) {\n throw new Error('无法获取有效的输出目录');\n }\n } catch (error) {\n if (error instanceof Error) {\n throw error;\n }\n throw new Error(`确定输出目录失败: ${String(error)}`);\n }\n\n // 使用统一的文件名获取方法\n const fileName = this._getFileName(url, options, defaultExt);\n\n // 验证文件名是否有效\n if (!fileName || fileName.trim().length === 0) {\n throw new Error('无法获取有效的文件名');\n }\n\n const finalPath = path.join(outputDir, fileName);\n\n // 验证最终路径是否有效\n if (!finalPath || finalPath.trim().length === 0) {\n throw new Error('无法构建有效的输出路径');\n }\n\n return finalPath;\n }\n}\n\n/**\n * 默认下载器实例(单例)\n */\nlet defaultDownloader: Downloader | null = null;\n\n/**\n * 获取默认下载器实例\n * @returns Downloader 实例\n */\nexport function getDownloader(): Downloader {\n if (!defaultDownloader) {\n defaultDownloader = new Downloader();\n }\n return defaultDownloader;\n}\n\n/**\n * 下载文件的便捷函数\n * @param url 要下载的 URL\n * @param options 下载选项\n * @param onProgress 进度回调函数(可选)\n * @returns Promise<DownloadResult> 下载结果\n */\nexport async function downloadFile(\n url: string,\n options: DownloadOptions = {},\n onProgress?: DownloadProgressCallback\n): Promise<DownloadResult> {\n const downloader = getDownloader();\n return downloader.download(url, options, onProgress);\n}\n\n"],"names":[],"mappings":"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"}
|
package/main/downloader.d.ts
CHANGED
|
@@ -69,6 +69,12 @@ export declare class Downloader {
|
|
|
69
69
|
private _downloadingUrls;
|
|
70
70
|
/** 上一次选择的保存文件夹路径 */
|
|
71
71
|
private _lastSelectedDir;
|
|
72
|
+
/** 缓存的下载目录路径 */
|
|
73
|
+
private _cachedDownloadsPath;
|
|
74
|
+
/** 目录可访问性缓存(避免重复检查) */
|
|
75
|
+
private _directoryAccessCache;
|
|
76
|
+
/** 目录缓存有效期(毫秒) */
|
|
77
|
+
private readonly _cacheTTL;
|
|
72
78
|
/**
|
|
73
79
|
* 构造函数:初始化下载器并注册 IPC 处理器
|
|
74
80
|
* 注册 `core:downloader` IPC 处理器,用于从渲染进程接收下载请求
|
|
@@ -132,7 +138,11 @@ export declare class Downloader {
|
|
|
132
138
|
*/
|
|
133
139
|
private _getFileName;
|
|
134
140
|
/**
|
|
135
|
-
*
|
|
141
|
+
* 获取下载目录路径(带缓存)
|
|
142
|
+
*/
|
|
143
|
+
private _getDownloadsPath;
|
|
144
|
+
/**
|
|
145
|
+
* 检查目录是否存在且有访问权限(带缓存优化)
|
|
136
146
|
*
|
|
137
147
|
* 检查项:
|
|
138
148
|
* 1. 路径是否有效(非空字符串)
|
|
@@ -157,6 +167,7 @@ export declare class Downloader {
|
|
|
157
167
|
* @param url 下载 URL(用于提取默认文件名)
|
|
158
168
|
* @param options 下载选项(包含 defaultPath、outputDir 等)
|
|
159
169
|
* @param defaultExt 默认扩展名(可选,用于 base64 URL)
|
|
170
|
+
* @param isMas 是否是 macOS App Store 环境(可选,用于跳过权限检查)
|
|
160
171
|
* @returns Promise<string | null> 用户选择的保存路径,如果取消则返回 null
|
|
161
172
|
*/
|
|
162
173
|
private _showSaveDialog;
|
|
@@ -166,18 +177,6 @@ export declare class Downloader {
|
|
|
166
177
|
* @returns 如果是 base64 URL,返回 true、文件扩展名、MIME 类型和数据;否则返回 false
|
|
167
178
|
*/
|
|
168
179
|
private _isBase64DataUrl;
|
|
169
|
-
/**
|
|
170
|
-
* 保存 base64 数据到文件
|
|
171
|
-
*
|
|
172
|
-
* 处理流程:
|
|
173
|
-
* 1. 将 base64 字符串解码为 Buffer
|
|
174
|
-
* 2. 将 Buffer 写入目标文件路径
|
|
175
|
-
*
|
|
176
|
-
* @param base64Data base64 编码的数据字符串
|
|
177
|
-
* @param filePath 目标文件路径(完整路径,包含文件名)
|
|
178
|
-
* @throws 如果解码失败或文件写入失败,抛出错误
|
|
179
|
-
*/
|
|
180
|
-
private _saveBase64ToFile;
|
|
181
180
|
/**
|
|
182
181
|
* 准备输出路径
|
|
183
182
|
*
|
|
@@ -207,7 +206,7 @@ export declare class Downloader {
|
|
|
207
206
|
*/
|
|
208
207
|
private _createErrorResult;
|
|
209
208
|
/**
|
|
210
|
-
*
|
|
209
|
+
* 从错误对象中提取错误类型和详细信息(性能优化版本)
|
|
211
210
|
* @param error 错误对象
|
|
212
211
|
* @param url 下载的 URL(用于上下文信息)
|
|
213
212
|
* @returns 包含错误类型和详细错误信息的对象
|
|
@@ -241,6 +240,10 @@ export declare class Downloader {
|
|
|
241
240
|
* - 如果文件已存在,添加数字后缀:filename(1).ext, filename(2).ext, ...
|
|
242
241
|
* - 最多尝试 MAX_FILENAME_COUNTER 次,防止无限循环
|
|
243
242
|
*
|
|
243
|
+
* 性能优化:
|
|
244
|
+
* - 使用二分查找优化查找范围(如果文件很多)
|
|
245
|
+
* - 限制最大检查次数
|
|
246
|
+
*
|
|
244
247
|
* 示例:
|
|
245
248
|
* - "test.pdf" -> "test.pdf" (如果不存在)
|
|
246
249
|
* - "test.pdf" -> "test(1).pdf" (如果 test.pdf 已存在)
|
package/main/downloader.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"downloader.d.ts","sourceRoot":"","sources":["../src/main/downloader.ts"],"names":[],"mappings":"AA4CA;;GAEG;AACH,MAAM,MAAM,wBAAwB,GAAG,CAAC,QAAQ,EAAE;IAChD,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACf,KAAK,IAAI,CAAC;AAEX;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,sBAAsB;IACtB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,mCAAmC;IACnC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4BAA4B;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+BAA+B;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0BAA0B;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,2BAA2B;IAC3B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,sBAAsB;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wBAAwB;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW;IACX,KAAK,CAAC,EAAE;QACN,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,mBAAmB;IACnB,UAAU,CAAC,EAAE,wBAAwB,CAAC;IACtC,mBAAmB;IACnB,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,mBAAmB;IACnB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,gBAAgB;IAChB,kBAAkB,CAAC,EAAE;QACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACjC,cAAc,CAAC,EAAE,OAAO,CAAC;QACzB,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,WAAW;IACX,OAAO,EAAE,OAAO,CAAC;IACjB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8BAA8B;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8BAA8B;IAC9B,SAAS,CAAC,EAAE,SAAS,GAAG,aAAa,GAAG,YAAY,GAAG,YAAY,GAAG,aAAa,GAAG,SAAS,CAAC;IAChG,qBAAqB;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,qBAAa,UAAU;IACrB,2BAA2B;IAC3B,OAAO,CAAC,gBAAgB,CAAuC;IAC/D,oBAAoB;IACpB,OAAO,CAAC,gBAAgB,CAAuB;
|
|
1
|
+
{"version":3,"file":"downloader.d.ts","sourceRoot":"","sources":["../src/main/downloader.ts"],"names":[],"mappings":"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"}
|