UNPKG

@xuanqikai/one-click-upload

Version:

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

245 lines 8.63 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.TOSUploader = void 0; const tos_sdk_1 = require("@volcengine/tos-sdk"); const fs = __importStar(require("fs-extra")); const path = __importStar(require("path")); const BaseUploader_1 = require("./BaseUploader"); class TOSUploader extends BaseUploader_1.BaseUploader { constructor(config) { super(config); this.initializeClient(); } /** * 初始化 TOS 客户端 */ initializeClient() { const tosConfig = this.config.config; this.client = new tos_sdk_1.TosClient({ region: tosConfig.region, accessKeyId: tosConfig.accessKeyId, accessKeySecret: tosConfig.accessKeySecret, endpoint: tosConfig.endpoint, // 设置超时时间 requestTimeout: 60000, connectionTimeout: 10000 }); } /** * 上传单个文件 */ async uploadSingleFile(localPath, remotePath, options) { const fileName = path.basename(localPath); const fileSize = await this.getFileSize(localPath); const tosConfig = this.config.config; // 构建完整的远程文件路径 let fullRemotePath = remotePath; if (remotePath.endsWith('/')) { // 如果远程路径以斜杠结尾,说明是目录,需要添加文件名 fullRemotePath = remotePath + fileName; } try { // 检查文件是否已存在 if (!options?.overwrite) { try { await this.client.headObject({ bucket: tosConfig.bucket, key: fullRemotePath }); // 文件已存在且不覆盖 return { success: false, fileName, localPath, remotePath: fullRemotePath, error: 'File already exists (use --overwrite to replace)', size: fileSize }; } catch (error) { // 文件不存在,继续上传 // 只有当错误是 NoSuchKey 或 404 时才继续,其他错误应该抛出 if (error.code !== 'NoSuchKey' && error.statusCode !== 404) { throw error; } } } const progressCallback = this.createProgressCallback(fileName, options?.onProgress); // 根据文件大小选择上传方式 const chunkSize = options?.chunkSize || this.defaultChunkSize; const useMultipartUpload = fileSize > chunkSize; if (useMultipartUpload) { // 使用分片上传 await this.client.uploadFile({ bucket: tosConfig.bucket, key: fullRemotePath, file: localPath, partSize: chunkSize, taskNum: 3, // 并发数 headers: this.getUploadHeaders(localPath, options?.customCacheControl) }); } else { // 使用普通上传 const fileContent = await fs.readFile(localPath); await this.client.putObject({ bucket: tosConfig.bucket, key: fullRemotePath, body: fileContent, headers: this.getUploadHeaders(localPath, options?.customCacheControl) }); // 手动触发进度回调 progressCallback(fileSize, fileSize); } // 构建文件 URL const url = this.buildFileUrl(fullRemotePath); return { success: true, fileName, localPath, remotePath: fullRemotePath, url, size: fileSize }; } catch (error) { return { success: false, fileName, localPath, remotePath: fullRemotePath, error: error.message || 'Unknown error', size: fileSize }; } } /** * 构建文件 URL */ buildFileUrl(remotePath) { const tosConfig = this.config.config; const endpoint = tosConfig.endpoint || `https://${tosConfig.bucket}.${tosConfig.region}.volces.com`; // 确保 endpoint 以 https:// 开头 const baseUrl = endpoint.startsWith('http') ? endpoint : `https://${endpoint}`; return `${baseUrl}/${remotePath}`; } /** * 验证配置 */ async validateConfig() { try { const tosConfig = this.config.config; // 尝试列出 bucket 信息来验证配置 await this.client.headBucket(tosConfig.bucket); return true; } catch (error) { console.error('TOS config validation failed:', error.message || 'Unknown error'); return false; } } /** * 检查文件是否存在 */ async fileExists(remotePath) { try { const tosConfig = this.config.config; await this.client.headObject({ bucket: tosConfig.bucket, key: remotePath }); return true; } catch (error) { // 只有当错误是 NoSuchKey 或 404 时返回 false,其他错误重新抛出 if (error.code === 'NoSuchKey' || error.statusCode === 404) { return false; } throw error; } } /** * 获取文件信息 */ async getFileInfo(remotePath) { try { const tosConfig = this.config.config; return await this.client.headObject({ bucket: tosConfig.bucket, key: remotePath }); } catch (error) { throw new Error(`Failed to get file info: ${error.message || 'Unknown error'}`); } } /** * 删除文件 */ async deleteFile(remotePath) { try { const tosConfig = this.config.config; await this.client.deleteObject({ bucket: tosConfig.bucket, key: remotePath }); return true; } catch (error) { console.error('Failed to delete file:', error.message || 'Unknown error'); return false; } } /** * 列出文件 */ async listFiles(prefix, maxKeys = 1000) { try { const tosConfig = this.config.config; const result = await this.client.listObjects({ bucket: tosConfig.bucket, prefix, maxKeys }); return result.Contents || []; } catch (error) { throw new Error(`Failed to list files: ${error.message || 'Unknown error'}`); } } } exports.TOSUploader = TOSUploader; //# sourceMappingURL=TOSUploader.js.map