UNPKG

@xuanqikai/one-click-upload

Version:

A CLI tool for one-click file upload to cloud storage services (OSS, TOS)

227 lines 7.72 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.DEFAULT_RETRY_CONFIG = void 0; exports.retryOperation = retryOperation; exports.sleep = sleep; exports.createProgressAggregator = createProgressAggregator; exports.validateUploadOptions = validateUploadOptions; exports.calculateUploadStats = calculateUploadStats; exports.shouldUploadFile = shouldUploadFile; exports.generateUploadSummary = generateUploadSummary; const path = __importStar(require("path")); /** * 默认重试配置 */ exports.DEFAULT_RETRY_CONFIG = { maxRetries: 3, baseDelay: 1000, // 1秒 maxDelay: 30000, // 30秒 backoffFactor: 2 }; /** * 执行带重试的操作 */ async function retryOperation(operation, config = {}) { const retryConfig = { ...exports.DEFAULT_RETRY_CONFIG, ...config }; let lastError; for (let attempt = 1; attempt <= retryConfig.maxRetries; attempt++) { try { return await operation(); } catch (error) { lastError = error; if (attempt === retryConfig.maxRetries) { throw lastError; } // 计算延迟时间(指数退避) const delay = Math.min(retryConfig.baseDelay * Math.pow(retryConfig.backoffFactor, attempt - 1), retryConfig.maxDelay); console.warn(`Attempt ${attempt} failed, retrying in ${delay}ms...`); await sleep(delay); } } throw lastError; } /** * 睡眠函数 */ function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } /** * 创建进度聚合器(用于批量上传) */ function createProgressAggregator(totalFiles, onProgress) { const fileProgresses = new Array(totalFiles).fill({ loaded: 0, total: 0 }); return (fileIndex, fileProgress) => { if (!onProgress) return; fileProgresses[fileIndex] = { loaded: fileProgress.loaded, total: fileProgress.total }; // 计算总进度 const totalLoaded = fileProgresses.reduce((sum, p) => sum + p.loaded, 0); const totalSize = fileProgresses.reduce((sum, p) => sum + p.total, 0); const overallProgress = { loaded: totalLoaded, total: totalSize, percentage: totalSize > 0 ? Math.round((totalLoaded / totalSize) * 100) : 0, fileName: `${fileIndex + 1}/${totalFiles} - ${fileProgress.fileName}` }; onProgress(overallProgress); }; } /** * 验证上传选项 */ function validateUploadOptions(options) { const errors = []; if (!options.localPath || !options.localPath.trim()) { errors.push('Local path is required'); } if (!options.remotePath || !options.remotePath.trim()) { errors.push('Remote path is required'); } if (options.chunkSize && options.chunkSize <= 0) { errors.push('Chunk size must be greater than 0'); } if (options.maxRetries && options.maxRetries < 0) { errors.push('Max retries must be non-negative'); } return { valid: errors.length === 0, errors }; } /** * 计算上传统计信息 */ function calculateUploadStats(results) { const totalFiles = results.length; const successCount = results.filter(r => r.success).length; const failedCount = totalFiles - successCount; const totalSize = results.reduce((sum, r) => sum + (r.size || 0), 0); const successRate = totalFiles > 0 ? (successCount / totalFiles) * 100 : 0; return { totalFiles, successCount, failedCount, totalSize, successRate }; } /** * 过滤文件(排除隐藏文件、临时文件等) */ function shouldUploadFile(filePath, options) { const fileName = path.basename(filePath); const opts = { includeHidden: false, excludePatterns: [], ...options }; // 排除隐藏文件 if (!opts.includeHidden && fileName.startsWith('.')) { return false; } // 排除临时文件 const tempExtensions = ['.tmp', '.temp', '.swp', '.bak']; if (tempExtensions.some(ext => fileName.endsWith(ext))) { return false; } // 排除系统文件 const systemFiles = ['Thumbs.db', 'Desktop.ini', '.DS_Store']; if (systemFiles.includes(fileName)) { return false; } // 自定义排除模式 if (opts.excludePatterns) { for (const pattern of opts.excludePatterns) { const regex = new RegExp(pattern); if (regex.test(fileName) || regex.test(filePath)) { return false; } } } return true; } /** * 生成上传摘要报告 */ function generateUploadSummary(result) { const { totalFiles, successCount, failedCount, totalSize, duration } = result; const successRate = totalFiles > 0 ? ((successCount / totalFiles) * 100).toFixed(1) : '0'; let summary = `Upload Summary:\n`; summary += `─────────────────────────────────────────\n`; summary += `Total files: ${totalFiles}\n`; summary += `Successful: ${successCount} (${successRate}%)\n`; summary += `Failed: ${failedCount}\n`; summary += `Total size: ${formatBytes(totalSize)}\n`; summary += `Duration: ${formatDuration(duration)}\n`; if (failedCount > 0) { summary += `\nFailed files:\n`; result.results .filter(r => !r.success) .forEach(r => { summary += ` ✗ ${r.fileName}: ${r.error}\n`; }); } return summary; } /** * 格式化字节数 */ function formatBytes(bytes) { const units = ['B', 'KB', 'MB', 'GB', 'TB']; let size = bytes; let unitIndex = 0; while (size >= 1024 && unitIndex < units.length - 1) { size /= 1024; unitIndex++; } return `${size.toFixed(2)} ${units[unitIndex]}`; } /** * 格式化持续时间 */ function formatDuration(milliseconds) { const seconds = Math.floor(milliseconds / 1000); const minutes = Math.floor(seconds / 60); const hours = Math.floor(minutes / 60); if (hours > 0) { return `${hours}h ${minutes % 60}m ${seconds % 60}s`; } else if (minutes > 0) { return `${minutes}m ${seconds % 60}s`; } else { return `${seconds}s`; } } //# sourceMappingURL=uploadUtils.js.map