UNPKG

@xuanqikai/one-click-upload

Version:

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

389 lines 13.7 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; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.OSSUploader = void 0; const ali_oss_1 = __importDefault(require("ali-oss")); const fs = __importStar(require("fs-extra")); const path = __importStar(require("path")); const BaseUploader_1 = require("./BaseUploader"); const CDNRefreshService_1 = require("../services/CDNRefreshService"); class OSSUploader extends BaseUploader_1.BaseUploader { constructor(config) { super(config); this.initializeClient(); this.initializeCDNService(); } /** * 初始化 OSS 客户端 */ initializeClient() { const ossConfig = this.config.config; this.client = new ali_oss_1.default({ region: ossConfig.region, accessKeyId: ossConfig.accessKeyId, accessKeySecret: ossConfig.accessKeySecret, bucket: ossConfig.bucket, endpoint: ossConfig.endpoint, // 启用 HTTPS secure: true, // 设置超时时间 timeout: 60000, }); } /** * 初始化 CDN 刷新服务 */ initializeCDNService() { const ossConfig = this.config.config; if (ossConfig.cdn && ossConfig.cdn.autoRefresh) { console.log("✅ CDN 配置完整,创建 CDN 服务..."); this.cdnService = new CDNRefreshService_1.CDNRefreshService(ossConfig.cdn); } else { console.log("❌ CDN 配置不完整或未启用自动刷新"); if (!ossConfig.cdn) { console.log("❌ 缺少 CDN 配置"); } else if (!ossConfig.cdn.autoRefresh) { console.log("❌ CDN 自动刷新未启用"); } } } /** * 上传单个文件 */ async uploadSingleFile(localPath, remotePath, options) { const fileName = path.basename(localPath); const fileSize = await this.getFileSize(localPath); // 构建完整的远程文件路径 let fullRemotePath = remotePath; if (remotePath.endsWith("/")) { // 如果远程路径以斜杠结尾,说明是目录,需要添加文件名 fullRemotePath = remotePath + fileName; } try { // 检查文件是否已存在 if (!options?.overwrite) { try { await this.client.head(fullRemotePath, {}); // 文件已存在且不覆盖 return { success: false, fileName, localPath, remotePath: fullRemotePath, error: "File already exists (use --overwrite to replace)", size: fileSize, }; } catch (error) { // 文件不存在,继续上传 // 只有当错误是 NoSuchKey 时才继续,其他错误应该抛出 if (error.code !== "NoSuchKey" && error.status !== 404) { throw error; } } } const progressCallback = this.createProgressCallback(fileName, options?.onProgress); // 根据文件大小选择上传方式 const chunkSize = options?.chunkSize || this.defaultChunkSize; const useMultipartUpload = fileSize > chunkSize; let result; if (useMultipartUpload) { // 使用分片上传 result = await this.client.multipartUpload(fullRemotePath, localPath, { partSize: chunkSize, headers: this.getUploadHeaders(localPath, options?.customCacheControl), }); } else { // 使用普通上传 const fileContent = await fs.readFile(localPath); result = await this.client.put(fullRemotePath, fileContent, { headers: this.getUploadHeaders(localPath, options?.customCacheControl), }); // 手动触发进度回调 progressCallback(fileSize, fileSize); } // 上传成功后,如果配置了 CDN 自动刷新,则刷新缓存 return { success: true, fileName, localPath, remotePath: fullRemotePath, url: result.url, size: fileSize, }; } catch (error) { return { success: false, fileName, localPath, remotePath: fullRemotePath, error: error.message || "Unknown error", size: fileSize, }; } } /** * 验证配置 */ async validateConfig() { try { // 尝试列出 bucket 信息来验证配置 const ossConfig = this.config.config; await this.client.getBucketInfo(ossConfig.bucket); return true; } catch (error) { console.error("OSS config validation failed:", error.message || "Unknown error"); return false; } } /** * 刷新 CDN 缓存(目录) */ async refreshCDNDirectory(remoteDirPath) { if (!this.cdnService) { console.warn("CDN service not configured or auto-refresh is disabled"); return false; } try { const cdnUrl = this.cdnService.buildCDNUrl(remoteDirPath); const result = await this.cdnService.refreshDirectory(cdnUrl); if (result.success) { console.log(`✅ CDN 目录缓存刷新成功: ${cdnUrl} (任务ID: ${result.taskId})`); return true; } else { console.error(`❌ CDN 目录缓存刷新失败: ${cdnUrl} - ${result.error}`); return false; } } catch (error) { console.error(`❌ CDN 目录缓存刷新异常: ${error.message}`); return false; } } /** * 检查文件是否存在 */ async fileExists(remotePath) { try { await this.client.head(remotePath, {}); return true; } catch (error) { // 只有当错误是 NoSuchKey 或 404 时返回 false,其他错误重新抛出 if (error.code === "NoSuchKey" || error.status === 404) { return false; } throw error; } } /** * 获取文件信息 */ async getFileInfo(remotePath) { try { return await this.client.head(remotePath, {}); } catch (error) { throw new Error(`Failed to get file info: ${error.message || "Unknown error"}`); } } /** * 删除文件 */ async deleteFile(remotePath) { try { await this.client.delete(remotePath); return true; } catch (error) { console.error("Failed to delete file:", error.message || "Unknown error"); return false; } } /** * 列出文件 */ async listFiles(prefix, maxKeys = 1000) { try { const result = await this.client.list({ prefix, "max-keys": maxKeys, }, {}); return result.objects || []; } catch (error) { throw new Error(`Failed to list files: ${error.message || "Unknown error"}`); } } /** * 查询 CDN 刷新任务状态 */ async getCDNRefreshTaskStatus(taskId) { if (!this.cdnService) { throw new Error("CDN service not configured"); } return await this.cdnService.getRefreshTaskStatus(taskId); } /** * 查询 CDN 刷新配额 */ async getCDNRefreshQuota() { if (!this.cdnService) { throw new Error("CDN service not configured"); } return await this.cdnService.getRefreshQuota(); } /** * 刷新 CDN 缓存(单个文件) */ async refreshCDNCache(remotePath) { if (!this.cdnService) { console.warn("CDN service not configured or auto-refresh is disabled"); return false; } try { const cdnUrl = this.cdnService.buildCDNUrl(remotePath); const result = await this.cdnService.refreshUrl(cdnUrl); if (result.success) { console.log(`✅ CDN 缓存刷新成功: ${cdnUrl} (任务ID: ${result.taskId})`); return true; } else { console.error(`❌ CDN 缓存刷新失败: ${cdnUrl} - ${result.error}`); return false; } } catch (error) { console.error(`❌ CDN 缓存刷新异常: ${error.message}`); return false; } } /** * 批量刷新 CDN 缓存 */ async refreshCDNCacheBatch(remotePaths) { if (!this.cdnService) { console.warn("CDN service not configured or auto-refresh is disabled"); return { success: 0, failed: remotePaths.length }; } try { const cdnUrls = remotePaths.map((p) => this.cdnService.buildCDNUrl(p)); const results = await this.cdnService.refreshUrls(cdnUrls); let successCount = 0; let failedCount = 0; results.forEach((result) => { if (result.success) { successCount++; } else { failedCount++; } }); return { success: successCount, failed: failedCount }; } catch (error) { console.error(`❌ CDN 缓存批量刷新异常: ${error.message}`); return { success: 0, failed: remotePaths.length }; } } /** * 预热 CDN 缓存(单个文件) */ async preloadCDNCache(remotePath, options) { if (!this.cdnService) { throw new Error("CDN service not configured or auto-refresh is disabled"); } try { const cdnUrl = this.cdnService.buildCDNUrl(remotePath); const result = await this.cdnService.preloadUrl(cdnUrl, options); if (result.success) { console.log(`✅ CDN 缓存预热成功: ${cdnUrl} (任务ID: ${result.taskId})`); return true; } else { console.error(`❌ CDN 缓存预热失败: ${cdnUrl} - ${result.error}`); return false; } } catch (error) { console.error(`❌ CDN 缓存预热异常: ${error.message}`); return false; } } /** * 批量预热 CDN 缓存(多个文件) */ async preloadCDNCacheBatch(remotePaths, options) { if (!this.cdnService) { throw new Error("CDN service not configured or auto-refresh is disabled"); } try { const cdnUrls = remotePaths.map((p) => this.cdnService.buildCDNUrl(p)); const results = await this.cdnService.preloadUrls(cdnUrls, options); let successCount = 0; let failedCount = 0; results.forEach((result) => { if (result.success) { successCount++; } else { failedCount++; } }); return { success: successCount, failed: failedCount }; } catch (error) { console.error(`❌ CDN 缓存批量预热异常: ${error.message}`); return { success: 0, failed: remotePaths.length }; } } /** * 检查是否启用了 CDN 自动刷新 */ isCDNAutoRefreshEnabled() { return !!this.cdnService; } } exports.OSSUploader = OSSUploader; //# sourceMappingURL=OSSUploader.js.map