@xuanqikai/one-click-upload
Version:
A CLI tool for one-click file upload to cloud storage services (OSS, TOS)
252 lines • 9.35 kB
JavaScript
;
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