import * as fs from 'fs'; import { resolve } from 'path'; import { execSync } from 'child_process'; import * as path from 'path'; import consola from 'consola'; import axios from 'axios'; import { URL } from 'url'; // 错误信息常量 const ErrorMessages = { NO_SOURCE: '请提供源文件路径或URL', INVALID_FILE_TYPE: '无效的文件类型,仅支持 PNG、JPG、JPEG 和 GIF', FILE_NOT_EXIST: '源文件不存在', DOWNLOAD_FAILED: '文件下载失败:', COPY_FAILED: '文件复制失败:', CONVERT_FAILED: '文件转换失败:', UNEXPECTED_ERROR: '发生意外错误:', }; // 配置常量 const CONFIG = { ALLOWED_EXTENSIONS: ['.png', '.jpg', '.jpeg', '.gif'], OUTPUT_FILENAME: 'logo.png', TEMP_PREFIX: 'temp-logo-' }; /** * 从URL下载文件到指定目录 * @param url 文件的URL地址 * @param destination 目标保存路径 */ async function downloadFile(url: string, destination: string): Promise { try { const response = await axios({ url, method: 'GET', responseType: 'stream', timeout: 10000 // 10秒超时 }); const writer = fs.createWriteStream(destination); response.data.pipe(writer); return new Promise((resolve, reject) => { writer.on('finish', resolve); writer.on('error', reject); }); } catch (error) { throw new Error(`${ErrorMessages.DOWNLOAD_FAILED} ${error.message}`); } } /** * 转换图片为PNG格式 * @param source 源文件路径 * @param destination 目标文件路径 */ function convertToPng(source: string, destination: string): void { try { // 使用 ImageMagick 转换图片格式 execSync(`convert "${source}" "${destination}"`); } catch (error) { throw new Error(`${ErrorMessages.CONVERT_FAILED} ${error.message}`); } } /** * 验证字符串是否为有效URL * @param string 待验证的字符串 */ function isValidUrl(string: string): boolean { try { new URL(string); return true; } catch { return false; } } /** * 验证文件是否为支持的图片类型 * @param filePath 文件路径 */ function isValidImageFile(filePath: string): boolean { const ext = path.extname(filePath).toLowerCase(); return CONFIG.ALLOWED_EXTENSIONS.includes(ext); } /** * 清理临时文件 * @param filePath 文件路径 */ function cleanupTempFile(filePath: string): void { if (fs.existsSync(filePath)) { try { fs.unlinkSync(filePath); } catch (error) { consola.warn('清理临时文件失败:', error.message); } } } /** * 主函数 - 处理Logo替换逻辑 */ async function main(): Promise { const tempFiles: string[] = []; try { // 获取命令行参数中的源文件路径 const sourcePath = process.argv[2]; if (!sourcePath) { throw new Error(ErrorMessages.NO_SOURCE); } const baseDir: string = resolve('./'); const finalDestination = path.join(baseDir, CONFIG.OUTPUT_FILENAME); // 打印初始信息 consola.info('开始处理Logo'); consola.info('源文件:', sourcePath); consola.info('目标位置:', finalDestination); // 验证文件类型 if (!isValidImageFile(sourcePath)) { throw new Error(ErrorMessages.INVALID_FILE_TYPE); } // 处理URL或本地文件 if (isValidUrl(sourcePath)) { consola.info('正在从URL下载文件...'); const tempFile = path.join(baseDir, CONFIG.TEMP_PREFIX + Date.now() + path.extname(sourcePath)); tempFiles.push(tempFile); // 下载文件 await downloadFile(sourcePath, tempFile); consola.success('文件下载完成'); // 转换为PNG(如果需要) if (path.extname(tempFile).toLowerCase() !== '.png') { consola.info('正在转换为PNG格式...'); convertToPng(tempFile, finalDestination); } else { fs.copyFileSync(tempFile, finalDestination); } } else { // 处理本地文件 const localPath = path.resolve(sourcePath); if (!fs.existsSync(localPath)) { throw new Error(ErrorMessages.FILE_NOT_EXIST); } // 转换为PNG(如果需要) if (path.extname(localPath).toLowerCase() !== '.png') { consola.info('正在转换为PNG格式...'); convertToPng(localPath, finalDestination); } else { fs.copyFileSync(localPath, finalDestination); } } consola.success('Logo处理完成'); consola.info(`文件已保存至: ${finalDestination}`); } catch (error) { consola.error('Logo处理失败:', error.message); process.exit(1); } finally { // 清理所有临时文件 tempFiles.forEach(cleanupTempFile); } } // 执行主函数 main().catch(error => { consola.error(ErrorMessages.UNEXPECTED_ERROR, error); process.exit(1); });