UNPKG

@xuanqikai/one-click-upload

Version:

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

252 lines 9.35 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.BaseUploader = void 0; const fs = __importStar(require("fs-extra")); const mime = __importStar(require("mime-types")); const path = __importStar(require("path")); const fileUtils_1 = require("../utils/fileUtils"); class BaseUploader { constructor(config) { this.maxRetries = 3; this.defaultChunkSize = 1024 * 1024; // 1MB this.config = config; } /** * 上传单个文件或目录 */ async upload(localPath, remotePath, options) { try { // 检查本地路径是否存在 if (!(await fs.pathExists(localPath))) { throw new Error(`Local path does not exist: ${localPath}`); } const stats = await fs.stat(localPath); if (stats.isDirectory()) { // 检查本地路径是否以斜杠结尾来决定上传模式 const contentsOnly = localPath.endsWith('/'); // 上传目录 const batchResult = await this.uploadDirectory(localPath, remotePath, options, contentsOnly); // 返回目录上传的汇总结果 return { success: batchResult.successCount > 0, fileName: path.basename(localPath), localPath, remotePath, error: batchResult.failedCount > 0 ? `${batchResult.failedCount} files failed` : undefined, size: batchResult.totalSize }; } else { // 上传单个文件 return await this.uploadSingleFile(localPath, remotePath, options); } } catch (error) { return { success: false, fileName: path.basename(localPath), localPath, remotePath, error: error.message || 'Unknown error', size: 0 }; } } /** * 批量上传文件 */ async uploadBatch(files, options) { const startTime = Date.now(); const results = []; let totalSize = 0; let successCount = 0; let failedCount = 0; for (let i = 0; i < files.length; i++) { const { localPath, remotePath } = files[i]; try { const result = await this.uploadSingleFile(localPath, remotePath, { ...options, onProgress: options?.onProgress ? (progress) => { // 调整进度信息,包含批量上传的整体进度 const overallProgress = { ...progress, percentage: ((i + progress.percentage / 100) / files.length) * 100 }; options.onProgress(overallProgress); } : undefined }); results.push(result); totalSize += result.size || 0; if (result.success) { successCount++; } else { failedCount++; } } catch (error) { const result = { success: false, fileName: path.basename(localPath), localPath, remotePath, error: error.message || 'Unknown error', size: 0 }; results.push(result); failedCount++; } } return { totalFiles: files.length, successCount, failedCount, results, totalSize, duration: Date.now() - startTime }; } /** * 上传目录 */ async uploadDirectory(localDirPath, remoteDirPath, options, contentsOnly = false) { // 扫描目录获取所有文件 const files = await (0, fileUtils_1.scanDirectory)(localDirPath); // 构建上传文件列表 const uploadFiles = files.map(file => { let remoteFilePath; if (contentsOnly) { // 只上传内容,不包含目录名称 // 标准化相对路径,确保使用正斜杠 const normalizedRelativePath = (0, fileUtils_1.normalizePath)(file.relativePath); remoteFilePath = (0, fileUtils_1.generateRemoteFilePath)(remoteDirPath, normalizedRelativePath); } else { // 上传整个目录,包含目录名称 const localDirName = path.basename(localDirPath); // 标准化路径,确保使用正斜杠 const normalizedRelativePath = (0, fileUtils_1.normalizePath)(file.relativePath); const combinedPath = (0, fileUtils_1.normalizePath)(path.join(localDirName, normalizedRelativePath)); remoteFilePath = (0, fileUtils_1.generateRemoteFilePath)(remoteDirPath, combinedPath); } return { localPath: file.path, remotePath: remoteFilePath }; }); return await this.uploadBatch(uploadFiles, options); } /** * 重试机制 */ async retryOperation(operation, maxRetries = this.maxRetries, delay = 1000) { let lastError; for (let attempt = 1; attempt <= maxRetries; attempt++) { try { return await operation(); } catch (error) { lastError = error; if (attempt === maxRetries) { throw lastError; } // 指数退避延迟 const waitTime = delay * Math.pow(2, attempt - 1); await this.sleep(waitTime); } } throw lastError; } /** * 睡眠函数 */ sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } /** * 获取文件大小 */ async getFileSize(filePath) { const stats = await fs.stat(filePath); return stats.size; } /** * 创建进度回调 */ createProgressCallback(fileName, onProgress) { if (!onProgress) { return () => { }; } return (loaded, total) => { const progress = { loaded, total, percentage: total > 0 ? Math.round((loaded / total) * 100) : 0, fileName }; onProgress(progress); }; } /** * 获取上传头信息 */ getUploadHeaders(localPath, customCacheControl) { const mimeType = mime.lookup(localPath) || 'application/octet-stream'; const fileName = path.basename(localPath).toLowerCase(); let metaData = { 'Content-Type': mimeType, }; // 缓存策略:优先使用自定义设置,否则使用智能默认策略 let cacheControl; if (customCacheControl) { cacheControl = customCacheControl; } else if (fileName === 'index.html') { metaData['Cache-Control'] = 'no-cache, no-store, must-revalidate'; } else if (fileName.endsWith('.html')) { metaData['Cache-Control'] = 'max-age=3600'; // HTML 文件缓存1小时 // } else if (fileName.match(/\.(js|css)$/)) { // // 默认不缓存 // cacheControl = 'max-age=31536000'; // JS/CSS 文件缓存1年 // } else if (fileName.match(/\.(jpg|jpeg|png|gif|svg|ico|webp)$/)) { // cacheControl = 'max-age=31536000'; // 图片文件缓存1年 } return metaData; } } exports.BaseUploader = BaseUploader; //# sourceMappingURL=BaseUploader.js.map