@be-link/cos 1.12.0-beta.1 → 1.12.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cos.js +9 -0
- package/bin/upload.js +330 -0
- package/dist/beLinkCos.d.ts.map +1 -1
- package/dist/index.cjs.js +0 -1
- package/dist/index.esm.js +0 -1
- package/package.json +15 -12
- package/dist/index.cjs.js.map +0 -1
- package/dist/index.esm.js.map +0 -1
package/bin/cos.js
ADDED
package/bin/upload.js
ADDED
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const https = require('https');
|
|
7
|
+
const COS = require('cos-nodejs-sdk-v5');
|
|
8
|
+
const { BUCKETS_CONFIG, REGION } = require('../dist/index.cjs.js');
|
|
9
|
+
|
|
10
|
+
const argv = process.argv.slice(2);
|
|
11
|
+
const [mode] = argv;
|
|
12
|
+
console.log('🚀 上传模式:', mode || 'development');
|
|
13
|
+
|
|
14
|
+
// 获取项目名称
|
|
15
|
+
const baseUrl = mode === 'production' ? 'project/prod/' : 'project/dev/';
|
|
16
|
+
const packageUrl = path.resolve(process.cwd(), './package.json');
|
|
17
|
+
const projectPackage = fs.readFileSync(packageUrl, { encoding: 'utf-8' });
|
|
18
|
+
const projectName = JSON.parse(projectPackage).name;
|
|
19
|
+
const projectRemoteUrl = baseUrl + projectName;
|
|
20
|
+
|
|
21
|
+
// 构建参数输出的地址
|
|
22
|
+
const distDirUrl = process.cwd() + '/dist';
|
|
23
|
+
|
|
24
|
+
// COS 实例(延迟初始化)
|
|
25
|
+
let cos = null;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 通过云函数获取临时密钥(参照浏览器端实现)
|
|
29
|
+
*/
|
|
30
|
+
function getTempCredentials(mode) {
|
|
31
|
+
return new Promise((resolve, reject) => {
|
|
32
|
+
const url =
|
|
33
|
+
mode === 'production'
|
|
34
|
+
? 'https://shield-60660-10-1304510571.sh.run.tcloudbase.com/config/get-cos-temp-secret'
|
|
35
|
+
: 'https://shield-74680-5-1304510571.sh.run.tcloudbase.com/config/get-cos-temp-secret';
|
|
36
|
+
|
|
37
|
+
const urlObj = new URL(url);
|
|
38
|
+
const options = {
|
|
39
|
+
hostname: urlObj.hostname,
|
|
40
|
+
path: urlObj.pathname,
|
|
41
|
+
method: 'POST',
|
|
42
|
+
headers: {
|
|
43
|
+
'Content-Type': 'application/json',
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const req = https.request(options, (res) => {
|
|
48
|
+
let data = '';
|
|
49
|
+
|
|
50
|
+
res.on('data', (chunk) => {
|
|
51
|
+
data += chunk;
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
res.on('end', () => {
|
|
55
|
+
try {
|
|
56
|
+
const result = JSON.parse(data);
|
|
57
|
+
const credentials = result?.data?.credentials;
|
|
58
|
+
|
|
59
|
+
if (!credentials) {
|
|
60
|
+
reject(new Error('获取临时密钥失败:返回数据格式错误'));
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
resolve({
|
|
65
|
+
TmpSecretId: credentials.tmpSecretId,
|
|
66
|
+
TmpSecretKey: credentials.tmpSecretKey,
|
|
67
|
+
SecurityToken: credentials.sessionToken,
|
|
68
|
+
StartTime: result.data.startTime,
|
|
69
|
+
ExpiredTime: result.data.expiredTime,
|
|
70
|
+
});
|
|
71
|
+
} catch (err) {
|
|
72
|
+
reject(new Error('解析临时密钥响应失败:' + err.message));
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
req.on('error', (err) => {
|
|
78
|
+
reject(new Error('请求临时密钥失败:' + err.message));
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
req.end();
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* 初始化 COS 实例
|
|
87
|
+
* 优先使用云函数临时密钥,降级到环境变量
|
|
88
|
+
*/
|
|
89
|
+
async function initCOS() {
|
|
90
|
+
if (cos) {
|
|
91
|
+
return cos;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
// 方式1:尝试通过云函数获取临时密钥(推荐)
|
|
96
|
+
console.log('🔐 正在获取临时密钥...');
|
|
97
|
+
const credentials = await getTempCredentials(mode || 'development');
|
|
98
|
+
console.log('✅ 临时密钥获取成功,有效期至:', new Date(credentials.ExpiredTime * 1000).toLocaleString());
|
|
99
|
+
|
|
100
|
+
cos = new COS({
|
|
101
|
+
getAuthorization: (options, callback) => {
|
|
102
|
+
callback({
|
|
103
|
+
TmpSecretId: credentials.TmpSecretId,
|
|
104
|
+
TmpSecretKey: credentials.TmpSecretKey,
|
|
105
|
+
SecurityToken: credentials.SecurityToken,
|
|
106
|
+
StartTime: credentials.StartTime,
|
|
107
|
+
ExpiredTime: credentials.ExpiredTime,
|
|
108
|
+
});
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
return cos;
|
|
113
|
+
} catch (err) {
|
|
114
|
+
// 方式2:降级到环境变量(用于本地开发或特殊情况)
|
|
115
|
+
console.warn('⚠️ 临时密钥获取失败:', err.message);
|
|
116
|
+
console.warn('⚠️ 尝试使用环境变量 COS_SECRET_ID 和 COS_SECRET_KEY');
|
|
117
|
+
|
|
118
|
+
const secretId = process.env.COS_SECRET_ID;
|
|
119
|
+
const secretKey = process.env.COS_SECRET_KEY;
|
|
120
|
+
|
|
121
|
+
if (!secretId || !secretKey) {
|
|
122
|
+
console.error('❌ 错误:未找到 COS 密钥配置');
|
|
123
|
+
console.error(' 请设置环境变量:');
|
|
124
|
+
console.error(' - COS_SECRET_ID');
|
|
125
|
+
console.error(' - COS_SECRET_KEY');
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
console.log('✅ 使用环境变量中的密钥');
|
|
130
|
+
cos = new COS({
|
|
131
|
+
SecretId: secretId,
|
|
132
|
+
SecretKey: secretKey,
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
return cos;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* 上传单个文件(支持 Promise 和重试)
|
|
141
|
+
*/
|
|
142
|
+
function upload(url, filename, retryCount = 0, maxRetries = 3) {
|
|
143
|
+
return new Promise((resolve, reject) => {
|
|
144
|
+
if (!url || !filename) {
|
|
145
|
+
reject(new Error('上传参数错误:url 和 filename 不能为空'));
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
cos.putObject(
|
|
150
|
+
{
|
|
151
|
+
Bucket: BUCKETS_CONFIG[mode || 'development'].name,
|
|
152
|
+
Region: REGION,
|
|
153
|
+
Key: projectRemoteUrl + filename /* 必须 */,
|
|
154
|
+
StorageClass: 'STANDARD',
|
|
155
|
+
ACL: 'public-read', // 设置为公开读,确保文件可访问
|
|
156
|
+
Body: fs.createReadStream(url), // 上传文件对象
|
|
157
|
+
// onProgress: function (progressData) {
|
|
158
|
+
// // 可选:显示上传进度
|
|
159
|
+
// // console.log('上传进度:', JSON.stringify(progressData))
|
|
160
|
+
// },
|
|
161
|
+
},
|
|
162
|
+
function (err, data) {
|
|
163
|
+
if (err) {
|
|
164
|
+
// 重试逻辑
|
|
165
|
+
if (retryCount < maxRetries) {
|
|
166
|
+
console.log(` ⚠️ 上传失败,正在重试 (${retryCount + 1}/${maxRetries}): ${filename}`);
|
|
167
|
+
setTimeout(
|
|
168
|
+
() => {
|
|
169
|
+
upload(url, filename, retryCount + 1, maxRetries)
|
|
170
|
+
.then(resolve)
|
|
171
|
+
.catch(reject);
|
|
172
|
+
},
|
|
173
|
+
1000 * (retryCount + 1),
|
|
174
|
+
); // 递增延迟
|
|
175
|
+
} else {
|
|
176
|
+
console.error(` ❌ 上传失败(已重试 ${maxRetries} 次): ${filename}`, err.message);
|
|
177
|
+
reject(err);
|
|
178
|
+
}
|
|
179
|
+
} else {
|
|
180
|
+
console.log(` ✅ 上传成功: ${filename}`);
|
|
181
|
+
resolve(data);
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
);
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function getAllFiles(directoryPath) {
|
|
189
|
+
const files = fs.readdirSync(directoryPath, { withFileTypes: true });
|
|
190
|
+
const fileNamesAndPaths = [];
|
|
191
|
+
|
|
192
|
+
files.forEach((file) => {
|
|
193
|
+
const filePath = path.join(directoryPath, file.name);
|
|
194
|
+
|
|
195
|
+
if (file.isFile()) {
|
|
196
|
+
fileNamesAndPaths.push({
|
|
197
|
+
fileName: file.name,
|
|
198
|
+
filePath: filePath,
|
|
199
|
+
});
|
|
200
|
+
} else if (file.isDirectory()) {
|
|
201
|
+
const subDirectoryFiles = getAllFiles(filePath);
|
|
202
|
+
fileNamesAndPaths.push(...subDirectoryFiles);
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
return fileNamesAndPaths;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* 删除远程项目目录
|
|
211
|
+
* 注意:deleteObject 只能删除单个文件,不能删除目录
|
|
212
|
+
* 如需删除整个目录,需要先列出所有文件再逐个删除
|
|
213
|
+
*/
|
|
214
|
+
function deleteProject() {
|
|
215
|
+
return new Promise((resolve, reject) => {
|
|
216
|
+
// 先获取目录下的所有文件
|
|
217
|
+
cos.getBucket(
|
|
218
|
+
{
|
|
219
|
+
Bucket: BUCKETS_CONFIG[mode || 'development'].name,
|
|
220
|
+
Region: REGION,
|
|
221
|
+
Prefix: projectRemoteUrl,
|
|
222
|
+
},
|
|
223
|
+
function (err, data) {
|
|
224
|
+
if (err) {
|
|
225
|
+
console.log('⚠️ 获取远程文件列表失败(可能目录不存在,继续上传)');
|
|
226
|
+
resolve([]); // 目录不存在时继续执行
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const files = data.Contents || [];
|
|
231
|
+
if (files.length === 0) {
|
|
232
|
+
console.log('📁 远程目录为空,无需删除');
|
|
233
|
+
resolve([]);
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
console.log(`🗑️ 正在删除 ${files.length} 个旧文件...`);
|
|
238
|
+
|
|
239
|
+
// 批量删除文件
|
|
240
|
+
cos.deleteMultipleObject(
|
|
241
|
+
{
|
|
242
|
+
Bucket: BUCKETS_CONFIG[mode || 'development'].name,
|
|
243
|
+
Region: REGION,
|
|
244
|
+
Objects: files.map((file) => ({ Key: file.Key })),
|
|
245
|
+
},
|
|
246
|
+
function (err, data) {
|
|
247
|
+
if (err) {
|
|
248
|
+
console.error('❌ 删除失败:', err.message);
|
|
249
|
+
reject(err);
|
|
250
|
+
} else {
|
|
251
|
+
console.log(`✅ 成功删除 ${files.length} 个旧文件`);
|
|
252
|
+
resolve(data);
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
);
|
|
256
|
+
},
|
|
257
|
+
);
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* 主函数:上传静态资源到 COS
|
|
263
|
+
*/
|
|
264
|
+
async function staticUploadToCos() {
|
|
265
|
+
const startTime = Date.now();
|
|
266
|
+
console.log('\n' + '='.repeat(60));
|
|
267
|
+
console.log('📦 开始上传静态资源到 COS');
|
|
268
|
+
console.log('='.repeat(60));
|
|
269
|
+
console.log(`📂 项目名称: ${projectName}`);
|
|
270
|
+
console.log(`🌍 环境模式: ${mode || 'development'}`);
|
|
271
|
+
console.log(`📍 远程路径: ${projectRemoteUrl}`);
|
|
272
|
+
console.log(`📁 本地目录: ${distDirUrl}`);
|
|
273
|
+
console.log('='.repeat(60) + '\n');
|
|
274
|
+
|
|
275
|
+
try {
|
|
276
|
+
// 1. 初始化 COS
|
|
277
|
+
await initCOS();
|
|
278
|
+
|
|
279
|
+
// 2. 检查本地文件
|
|
280
|
+
if (!fs.existsSync(distDirUrl)) {
|
|
281
|
+
throw new Error(`本地目录不存在: ${distDirUrl}`);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const files = getAllFiles(distDirUrl);
|
|
285
|
+
if (files.length === 0) {
|
|
286
|
+
console.log('⚠️ 警告:没有找到需要上传的文件');
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
console.log(`📋 找到 ${files.length} 个文件待上传\n`);
|
|
291
|
+
|
|
292
|
+
// 3. 删除远程旧文件
|
|
293
|
+
await deleteProject();
|
|
294
|
+
|
|
295
|
+
// 4. 上传所有文件
|
|
296
|
+
console.log('\n📤 开始上传文件...\n');
|
|
297
|
+
const uploadPromises = files.map((file) => {
|
|
298
|
+
const filename = file.filePath.replace(distDirUrl, '');
|
|
299
|
+
return upload(file.filePath, filename);
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
// 等待所有文件上传完成
|
|
303
|
+
const results = await Promise.all(uploadPromises);
|
|
304
|
+
|
|
305
|
+
// 5. 输出结果
|
|
306
|
+
const endTime = Date.now();
|
|
307
|
+
const duration = ((endTime - startTime) / 1000).toFixed(2);
|
|
308
|
+
|
|
309
|
+
console.log('\n' + '='.repeat(60));
|
|
310
|
+
console.log('🎉 上传完成!');
|
|
311
|
+
console.log('='.repeat(60));
|
|
312
|
+
console.log(`✅ 成功上传: ${results.length} 个文件`);
|
|
313
|
+
console.log(`⏱️ 耗时: ${duration} 秒`);
|
|
314
|
+
console.log(`🔗 访问地址: https://${BUCKETS_CONFIG[mode || 'development'].host}/${projectRemoteUrl}`);
|
|
315
|
+
console.log('='.repeat(60) + '\n');
|
|
316
|
+
|
|
317
|
+
process.exit(0);
|
|
318
|
+
} catch (err) {
|
|
319
|
+
console.error('\n' + '='.repeat(60));
|
|
320
|
+
console.error('💥 上传失败!');
|
|
321
|
+
console.error('='.repeat(60));
|
|
322
|
+
console.error('❌ 错误信息:', err.message);
|
|
323
|
+
console.error('='.repeat(60) + '\n');
|
|
324
|
+
process.exit(1);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
module.exports = {
|
|
329
|
+
staticUploadToCos,
|
|
330
|
+
};
|
package/dist/beLinkCos.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"beLinkCos.d.ts","sourceRoot":"","sources":["../src/beLinkCos.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"beLinkCos.d.ts","sourceRoot":"","sources":["../src/beLinkCos.ts"],"names":[],"mappings":"AAGA,OAAO,EAA0B,KAAK,OAAO,EAAE,MAAM,UAAU,CAAC;AAChE,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEtE;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,SAAS;IACpB,iBAAiB;IACjB,OAAO,CAAC,GAAG,CAAa;IACxB,kBAAkB;IAClB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsC;IAC/D,aAAa;IACN,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAM;IACzC,aAAa;IACN,IAAI,EAAE,OAAO,GAAG,IAAI,CAAQ;IACnC,oBAAoB;IACpB,OAAO,CAAC,UAAU,CAAkB;IACpC,aAAa;IACb,SAAS,CAAC,aAAa,EAAE,OAAO,CAAS;IACzC,SAAS,CAAC,WAAW,EAAE,OAAO,CAAS;IACvC,WAAW;IACX,OAAO,CAAC,KAAK,CAAkB;IAE/B;;;;;;;;;;;;;;;;;;;;OAoBG;gBACS,MAAM,CAAC,EAAE,UAAU;IAO/B;;;;OAIG;IACH,IAAI,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAyB9B;;;;;;;OAOG;YACW,OAAO;IAqCrB;;OAEG;IACH,SAAS,CAAC,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAYjD;;OAEG;IACH,SAAS,CAAC,GAAG,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI;IAMvC,SAAS,CAAC,IAAI,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI;IAMxC,SAAS,CAAC,KAAK,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI;IAMzC;;OAEG;IACH,SAAS,CAAC,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO;IAmBrD;;OAEG;IACH,SAAS,IAAI;QAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAAC,UAAU,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI;IAY/F;;;;;;;;;;;;;;OAcG;IACI,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,SAAS,GAAE,MAAwB,GAAG,OAAO,CAAC,MAAM,CAAC;IA+DtF;;;;;;;;OAQG;IACH,OAAO,CAAC,WAAW;IASnB;;;;;;;;;;;OAWG;IACU,YAAY,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAS7D;;;;;;;;;;;;;;;;;;;;OAoBG;IACU,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC;IAsCjF;;OAEG;IACH,OAAO,IAAI,IAAI;CAsBhB"}
|
package/dist/index.cjs.js
CHANGED
package/dist/index.esm.js
CHANGED
package/package.json
CHANGED
|
@@ -1,18 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@be-link/cos",
|
|
3
|
-
"version": "1.12.0-beta.
|
|
3
|
+
"version": "1.12.0-beta.3",
|
|
4
4
|
"description": "前端项目产物上传cos",
|
|
5
|
-
"author": "zhuifeng <yangyiboys@163.com>",
|
|
6
5
|
"homepage": "https://github.com/snowmountain-top/be-link#readme",
|
|
6
|
+
"author": "zhuiyi",
|
|
7
7
|
"license": "ISC",
|
|
8
|
-
"main": "dist/index.cjs.js",
|
|
9
|
-
"module": "dist/index.esm.js",
|
|
10
|
-
"types": "dist/index.d.ts",
|
|
11
|
-
"files": [
|
|
12
|
-
"dist",
|
|
13
|
-
"package.json",
|
|
14
|
-
"README.md"
|
|
15
|
-
],
|
|
16
8
|
"repository": {
|
|
17
9
|
"type": "git",
|
|
18
10
|
"url": "git+https://github.com/snowmountain-top/be-link.git"
|
|
@@ -20,17 +12,28 @@
|
|
|
20
12
|
"bugs": {
|
|
21
13
|
"url": "https://github.com/snowmountain-top/be-link/issues"
|
|
22
14
|
},
|
|
15
|
+
"main": "dist/index.cjs.js",
|
|
16
|
+
"module": "dist/index.esm.js",
|
|
17
|
+
"types": "dist/index.d.ts",
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"bin"
|
|
21
|
+
],
|
|
22
|
+
"bin": {
|
|
23
|
+
"cos": "./bin/cos.js"
|
|
24
|
+
},
|
|
23
25
|
"peerDependencies": {
|
|
24
26
|
"cos-js-sdk-v5": "^1.8.6",
|
|
25
27
|
"crypto-js": "^4.2.0"
|
|
26
28
|
},
|
|
27
29
|
"devDependencies": {
|
|
28
|
-
"@types/crypto-js": "^4.2.2"
|
|
30
|
+
"@types/crypto-js": "^4.2.2",
|
|
31
|
+
"cos-js-sdk-v5": "^1.8.6",
|
|
32
|
+
"crypto-js": "^4.2.0"
|
|
29
33
|
},
|
|
30
34
|
"publishConfig": {
|
|
31
35
|
"access": "public"
|
|
32
36
|
},
|
|
33
|
-
"gitHead": "76b71bb6850e5f57f1a1ba4bd17f2ae0f603b67e",
|
|
34
37
|
"scripts": {
|
|
35
38
|
"clean": "rimraf ./dist && rimraf .rollup.cache",
|
|
36
39
|
"build:js": "rollup --config rollup.config.ts --configPlugin typescript",
|
package/dist/index.cjs.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs.js","sources":["../src/config.ts","../src/beLinkCos.ts"],"sourcesContent":["/**\n * 环境模式类型\n * - development: 开发环境\n * - test: 测试环境\n * - production: 生产环境\n */\nexport type EnvMode = 'development' | 'test' | 'production';\n\n/**\n * COS 存储桶配置接口\n */\nexport interface BucketConfig {\n /** 存储桶名称 */\n name: string;\n /** 存储桶访问域名 */\n host: string;\n /** 访问协议 */\n protocol: string;\n}\n\n/**\n * 各环境的 COS 存储桶配置\n * 根据环境自动切换到对应的存储桶\n */\nexport const BUCKETS_CONFIG: Record<EnvMode, BucketConfig> = {\n development: {\n name: 'dev-1304510571',\n host: 'dev-1304510571.cos.ap-nanjing.myqcloud.com',\n protocol: 'https',\n },\n test: {\n name: 'dev-1304510571',\n host: 'dev-1304510571.cos.ap-nanjing.myqcloud.com',\n protocol: 'https',\n },\n production: {\n name: 'release-1304510571',\n host: 'release-1304510571.file.myqcloud.com',\n protocol: 'https',\n },\n};\n\n/** COS 存储桶所在地域(南京) */\nexport const REGION = 'ap-nanjing' as const;\n","import CryptoJS from 'crypto-js';\n// @ts-ignore\nimport COS from 'cos-js-sdk-v5';\n\nimport { BUCKETS_CONFIG, REGION, type EnvMode } from './config';\nimport type { InitConfig, UploadConfig, UploadResult } from './types';\n\n/**\n * BeLinkCOS 类 - 腾讯云 COS 文件上传工具\n *\n * 功能:文件上传、MD5 计算、临时密钥授权\n *\n * @example\n * import { BeLinkCOS } from '@be-link/cos';\n *\n * // 方式一:构造函数传入配置,立即初始化\n * const uploader = new BeLinkCOS({ mode: 'production' });\n * await uploader.uploadFile(file);\n *\n * // 方式二:先创建实例,稍后初始化(支持异步配置)\n * const uploader = new BeLinkCOS();\n * const config = await fetchConfig(); // 从服务器获取配置\n * uploader.init({ mode: 'production' });\n * await uploader.uploadFile(file);\n */\nexport class BeLinkCOS {\n /** COS SDK 实例 */\n private cos: any = null;\n /** COS 存储的基础路径 */\n private readonly basePath: string = '/beLinkAllSource/static/';\n /** 自定义请求头 */\n public headers: Record<string, any> = {};\n /** 当前环境模式 */\n public mode: EnvMode | null = null;\n /** 是否限制临时密钥的使用范围 */\n private scopeLimit: boolean = false;\n /** 生命周期状态 */\n protected isInitialized: boolean = false;\n protected isDestroyed: boolean = false;\n /** 调试模式 */\n private debug: boolean = false;\n\n /**\n * 构造函数\n *\n * 支持两种使用方式:\n * 1. 立即初始化:传入配置对象,构造函数中自动调用 init()\n * 2. 延迟初始化:不传配置,稍后调用 init() 方法(支持异步配置)\n *\n * @param config - 可选的初始化配置\n * @param config.mode - **必填** 环境模式:'development' | 'test' | 'production'\n * @param config.headers - 可选,自定义请求头\n * @param config.ScopeLimit - 可选,是否限制临时密钥的使用范围(默认 false)\n * @param config.debug - 可选,是否开启调试模式(默认 false)\n *\n * @example\n * // 立即初始化\n * const uploader = new BeLinkCOS({ mode: 'production' });\n *\n * // 延迟初始化\n * const uploader = new BeLinkCOS();\n * // ... 稍后调用 uploader.init(config);\n */\n constructor(config?: InitConfig) {\n // 如果传入配置,立即初始化\n if (config) {\n this.init(config);\n }\n }\n\n /**\n * 初始化 COS 实例\n * @param config 配置对象\n * @description 可以在创建实例时通过构造函数传入配置,也可以稍后调用此方法初始化\n */\n init(config: InitConfig): void {\n if (this.isDestroyed) {\n this.warn('实例已销毁,无法初始化');\n return;\n }\n\n if (this.isInitialized) {\n this.warn('实例已初始化,请勿重复调用');\n return;\n }\n\n if (!this.validateConfig(config)) {\n return;\n }\n\n // 保存配置\n this.mode = config.mode;\n this.headers = config.headers || {};\n this.scopeLimit = config.ScopeLimit || false;\n this.debug = config.debug || false;\n this.isInitialized = true;\n\n this.log('COS 实例初始化成功', { mode: this.mode });\n }\n\n /**\n * 初始化 COS SDK 实例\n *\n * 内部方法,通过云函数获取临时密钥\n * 首次上传时会自动调用\n *\n * @returns Promise<void>\n */\n private async initCOS(): Promise<void> {\n if (this.cos) {\n return; // 已初始化,直接返回\n }\n\n const cos = new COS({\n getAuthorization: (_options: any, callback: (p: any) => void) => {\n const url =\n this.mode === 'production'\n ? 'https://shield-60660-10-1304510571.sh.run.tcloudbase.com/config/get-cos-temp-secret'\n : 'https://shield-74680-5-1304510571.sh.run.tcloudbase.com/config/get-cos-temp-secret';\n fetch(url, { method: 'POST' })\n .then((response) => response.json())\n .then((result: any) => {\n const data = result?.data || {};\n const credentials = data?.credentials;\n\n callback({\n TmpSecretId: credentials.tmpSecretId, // 临时密钥的 tmpSecretId\n TmpSecretKey: credentials.tmpSecretKey, // 临时密钥的 tmpSecretKey\n SecurityToken: credentials.sessionToken, // 临时密钥的 sessionToken\n // 建议返回服务器时间作为签名的开始时间,避免用户浏览器本地时间偏差过大导致签名错误\n StartTime: data.startTime, // 时间戳,单位秒,如:1580000000\n ExpiredTime: data.expiredTime, // 临时密钥失效时间戳,是申请临时密钥时,时间戳加 durationSeconds\n ScopeLimit: this.scopeLimit, // 细粒度控制权限需要设为 true,会限制密钥只在相同请求时重复使用\n });\n })\n .catch((error) => {\n this.error('获取临时密钥失败:', error);\n callback(null);\n });\n },\n });\n this.cos = cos;\n this.log('COS SDK 初始化成功');\n }\n\n /**\n * 检查实例状态\n */\n protected checkState(methodName: string): boolean {\n if (this.isDestroyed) {\n this.warn(`实例已销毁,无法调用 ${methodName}`);\n return false;\n }\n if (!this.isInitialized) {\n this.warn(`实例未初始化,请先调用 init 方法`);\n return false;\n }\n return true;\n }\n\n /**\n * 内部日志方法\n */\n protected log(...args: unknown[]): void {\n if (this.debug) {\n console.log(`[${this.constructor.name}]`, ...args);\n }\n }\n\n protected warn(...args: unknown[]): void {\n if (this.debug) {\n console.warn(`[${this.constructor.name}]`, ...args);\n }\n }\n\n protected error(...args: unknown[]): void {\n if (this.debug) {\n console.error(`[${this.constructor.name}]`, ...args);\n }\n }\n\n /**\n * 验证配置\n */\n protected validateConfig(config: InitConfig): boolean {\n if (!config) {\n this.error('配置对象不能为空');\n return false;\n }\n\n if (!config.mode) {\n this.error('mode 是必需的');\n return false;\n }\n\n if (!['development', 'test', 'production'].includes(config.mode)) {\n this.error('mode 必须是 development、test 或 production');\n return false;\n }\n\n return true;\n }\n\n /**\n * 获取配置\n */\n getConfig(): { mode: EnvMode | null; headers: Record<string, any>; scopeLimit: boolean } | null {\n if (!this.isInitialized) {\n this.warn('实例未初始化');\n return null;\n }\n return {\n mode: this.mode,\n headers: this.headers,\n scopeLimit: this.scopeLimit,\n };\n }\n\n /**\n * 计算文件的 MD5 哈希值\n *\n * 性能优化:\n * - 小文件(< 10MB):一次性读取,速度更快\n * - 大文件:分片读取,避免内存溢出\n *\n * @param file - 要计算 MD5 的文件对象\n * @param chunkSize - 分片大小(字节),默认 2MB\n * @returns Promise<string> 文件的 MD5 哈希值\n *\n * @example\n * const md5 = await beLinkCOS.createFileMd5(file);\n * console.log('文件 MD5:', md5);\n */\n public createFileMd5(file: File, chunkSize: number = 2 * 1024 * 1024): Promise<string> {\n return new Promise((resolve, reject) => {\n // 对于小文件(< 10MB),使用简单方法\n if (file.size < 10 * 1024 * 1024) {\n const reader = new FileReader();\n\n reader.onload = (e: ProgressEvent<FileReader>) => {\n const arrayBuffer = e.target?.result as ArrayBuffer;\n if (arrayBuffer) {\n const wordArray = CryptoJS.lib.WordArray.create(arrayBuffer as any);\n const hash = CryptoJS.MD5(wordArray).toString();\n resolve(hash);\n } else {\n reject(new Error('Failed to read file'));\n }\n };\n\n reader.onerror = (error: ProgressEvent<FileReader>) => {\n reject(error);\n };\n\n reader.readAsArrayBuffer(file);\n return;\n }\n\n // 对于大文件,使用分片读取\n let currentChunk = 0;\n const chunks = Math.ceil(file.size / chunkSize);\n const spark = CryptoJS.algo.MD5.create();\n const reader = new FileReader();\n\n reader.onload = (e: ProgressEvent<FileReader>) => {\n const arrayBuffer = e.target?.result as ArrayBuffer;\n if (arrayBuffer) {\n const wordArray = CryptoJS.lib.WordArray.create(arrayBuffer as any);\n spark.update(wordArray);\n\n currentChunk++;\n\n if (currentChunk < chunks) {\n loadNext();\n } else {\n const hash = spark.finalize().toString();\n resolve(hash);\n }\n }\n };\n\n reader.onerror = (error: ProgressEvent<FileReader>) => {\n reject(error);\n };\n\n const loadNext = () => {\n const start = currentChunk * chunkSize;\n const end = Math.min(start + chunkSize, file.size);\n const blob = file.slice(start, end);\n reader.readAsArrayBuffer(blob);\n };\n\n loadNext();\n });\n }\n\n /**\n * 生成文件在 COS 中的存储路径\n *\n * 路径规则:/beLinkAllSource/static/{文件类型}/{文件名}_{时间戳}.{扩展名}\n * 例如:/beLinkAllSource/static/image/avatar_1699999999999.png\n *\n * @param file - 文件对象\n * @returns string COS 存储路径\n */\n private getFilePath(file: File): string {\n const filename = file.name;\n const fileType = file?.type ? file.type.split('/')[0] : 'other';\n const fileExtension = filename.split('.').pop() || '';\n const filenameDateNow = `${filename.split('.')[0]}_${Date.now()}`;\n const newFileName = `${filenameDateNow}.${fileExtension}`;\n return `${this.basePath}${fileType}/${newFileName}`;\n }\n\n /**\n * 获取文件上传后的访问 URL(不执行实际上传)\n *\n * 用于在上传前预览文件的最终 URL\n *\n * @param file - 文件对象\n * @returns Promise<string | null> 文件的完整访问 URL,如果实例未初始化则返回 null\n *\n * @example\n * const url = await beLinkCOS.getSourceUrl(file);\n * console.log('文件将上传到:', url);\n */\n public async getSourceUrl(file: File): Promise<string | null> {\n if (!this.checkState('getSourceUrl')) {\n return null;\n }\n\n const path = this.getFilePath(file);\n return `https://${BUCKETS_CONFIG[this.mode!].host}${path}`;\n }\n\n /**\n * 上传文件到腾讯云 COS\n *\n * 核心功能:将文件上传到对应环境的 COS 存储桶\n * 支持上传进度回调、自定义请求头等配置\n *\n * @param file - 要上传的文件对象\n * @param config - 上传配置(可选)\n * @param config.Headers - 自定义请求头\n * @param config.onProgressCallback - 上传进度回调函数\n * @returns Promise<UploadResult> 上传结果,包含文件 URL、ETag 等信息\n *\n * @example\n * const uploader = new BeLinkCOS({ mode: 'production' });\n * const result = await uploader.uploadFile(file, {\n * onProgressCallback: (progressData) => {\n * console.log('进度:', progressData.percent * 100 + '%');\n * }\n * });\n * console.log('文件 URL:', result.url);\n */\n public async uploadFile(file: File, config?: UploadConfig): Promise<UploadResult> {\n if (!this.checkState('uploadFile')) {\n throw new Error('实例未初始化或已销毁');\n }\n\n // 首次上传时自动初始化 COS SDK\n if (!this.cos) {\n await this.initCOS();\n }\n\n const key = this.getFilePath(file);\n this.log('开始上传文件:', { filename: file.name, key });\n\n return new Promise((resolve, reject) => {\n this.cos?.putObject(\n {\n Bucket: BUCKETS_CONFIG[this.mode!].name,\n Region: REGION,\n Key: key,\n Body: file,\n onProgress: config?.onProgressCallback,\n ...config,\n Headers: { ...this.headers, ...(config?.Headers || {}) },\n },\n (err: any, data: UploadResult) => {\n if (err) {\n this.error('上传失败:', err);\n reject(err);\n } else {\n const url = `https://${data.Location}`;\n this.log('上传成功:', url);\n resolve({ ...data, url });\n }\n },\n );\n });\n }\n\n /**\n * 销毁实例\n */\n destroy(): void {\n if (this.isDestroyed) {\n this.warn('实例已销毁');\n return;\n }\n\n try {\n // 清理 COS 实例\n this.cos = null;\n\n // 重置状态\n this.isDestroyed = true;\n this.isInitialized = false;\n this.mode = null;\n this.headers = {};\n this.scopeLimit = false;\n\n this.log('实例已销毁');\n } catch (error) {\n this.error('销毁实例失败:', error);\n }\n }\n}\n"],"names":[],"mappings":"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"}
|
package/dist/index.esm.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.esm.js","sources":["../src/config.ts","../src/beLinkCos.ts"],"sourcesContent":["/**\n * 环境模式类型\n * - development: 开发环境\n * - test: 测试环境\n * - production: 生产环境\n */\nexport type EnvMode = 'development' | 'test' | 'production';\n\n/**\n * COS 存储桶配置接口\n */\nexport interface BucketConfig {\n /** 存储桶名称 */\n name: string;\n /** 存储桶访问域名 */\n host: string;\n /** 访问协议 */\n protocol: string;\n}\n\n/**\n * 各环境的 COS 存储桶配置\n * 根据环境自动切换到对应的存储桶\n */\nexport const BUCKETS_CONFIG: Record<EnvMode, BucketConfig> = {\n development: {\n name: 'dev-1304510571',\n host: 'dev-1304510571.cos.ap-nanjing.myqcloud.com',\n protocol: 'https',\n },\n test: {\n name: 'dev-1304510571',\n host: 'dev-1304510571.cos.ap-nanjing.myqcloud.com',\n protocol: 'https',\n },\n production: {\n name: 'release-1304510571',\n host: 'release-1304510571.file.myqcloud.com',\n protocol: 'https',\n },\n};\n\n/** COS 存储桶所在地域(南京) */\nexport const REGION = 'ap-nanjing' as const;\n","import CryptoJS from 'crypto-js';\n// @ts-ignore\nimport COS from 'cos-js-sdk-v5';\n\nimport { BUCKETS_CONFIG, REGION, type EnvMode } from './config';\nimport type { InitConfig, UploadConfig, UploadResult } from './types';\n\n/**\n * BeLinkCOS 类 - 腾讯云 COS 文件上传工具\n *\n * 功能:文件上传、MD5 计算、临时密钥授权\n *\n * @example\n * import { BeLinkCOS } from '@be-link/cos';\n *\n * // 方式一:构造函数传入配置,立即初始化\n * const uploader = new BeLinkCOS({ mode: 'production' });\n * await uploader.uploadFile(file);\n *\n * // 方式二:先创建实例,稍后初始化(支持异步配置)\n * const uploader = new BeLinkCOS();\n * const config = await fetchConfig(); // 从服务器获取配置\n * uploader.init({ mode: 'production' });\n * await uploader.uploadFile(file);\n */\nexport class BeLinkCOS {\n /** COS SDK 实例 */\n private cos: any = null;\n /** COS 存储的基础路径 */\n private readonly basePath: string = '/beLinkAllSource/static/';\n /** 自定义请求头 */\n public headers: Record<string, any> = {};\n /** 当前环境模式 */\n public mode: EnvMode | null = null;\n /** 是否限制临时密钥的使用范围 */\n private scopeLimit: boolean = false;\n /** 生命周期状态 */\n protected isInitialized: boolean = false;\n protected isDestroyed: boolean = false;\n /** 调试模式 */\n private debug: boolean = false;\n\n /**\n * 构造函数\n *\n * 支持两种使用方式:\n * 1. 立即初始化:传入配置对象,构造函数中自动调用 init()\n * 2. 延迟初始化:不传配置,稍后调用 init() 方法(支持异步配置)\n *\n * @param config - 可选的初始化配置\n * @param config.mode - **必填** 环境模式:'development' | 'test' | 'production'\n * @param config.headers - 可选,自定义请求头\n * @param config.ScopeLimit - 可选,是否限制临时密钥的使用范围(默认 false)\n * @param config.debug - 可选,是否开启调试模式(默认 false)\n *\n * @example\n * // 立即初始化\n * const uploader = new BeLinkCOS({ mode: 'production' });\n *\n * // 延迟初始化\n * const uploader = new BeLinkCOS();\n * // ... 稍后调用 uploader.init(config);\n */\n constructor(config?: InitConfig) {\n // 如果传入配置,立即初始化\n if (config) {\n this.init(config);\n }\n }\n\n /**\n * 初始化 COS 实例\n * @param config 配置对象\n * @description 可以在创建实例时通过构造函数传入配置,也可以稍后调用此方法初始化\n */\n init(config: InitConfig): void {\n if (this.isDestroyed) {\n this.warn('实例已销毁,无法初始化');\n return;\n }\n\n if (this.isInitialized) {\n this.warn('实例已初始化,请勿重复调用');\n return;\n }\n\n if (!this.validateConfig(config)) {\n return;\n }\n\n // 保存配置\n this.mode = config.mode;\n this.headers = config.headers || {};\n this.scopeLimit = config.ScopeLimit || false;\n this.debug = config.debug || false;\n this.isInitialized = true;\n\n this.log('COS 实例初始化成功', { mode: this.mode });\n }\n\n /**\n * 初始化 COS SDK 实例\n *\n * 内部方法,通过云函数获取临时密钥\n * 首次上传时会自动调用\n *\n * @returns Promise<void>\n */\n private async initCOS(): Promise<void> {\n if (this.cos) {\n return; // 已初始化,直接返回\n }\n\n const cos = new COS({\n getAuthorization: (_options: any, callback: (p: any) => void) => {\n const url =\n this.mode === 'production'\n ? 'https://shield-60660-10-1304510571.sh.run.tcloudbase.com/config/get-cos-temp-secret'\n : 'https://shield-74680-5-1304510571.sh.run.tcloudbase.com/config/get-cos-temp-secret';\n fetch(url, { method: 'POST' })\n .then((response) => response.json())\n .then((result: any) => {\n const data = result?.data || {};\n const credentials = data?.credentials;\n\n callback({\n TmpSecretId: credentials.tmpSecretId, // 临时密钥的 tmpSecretId\n TmpSecretKey: credentials.tmpSecretKey, // 临时密钥的 tmpSecretKey\n SecurityToken: credentials.sessionToken, // 临时密钥的 sessionToken\n // 建议返回服务器时间作为签名的开始时间,避免用户浏览器本地时间偏差过大导致签名错误\n StartTime: data.startTime, // 时间戳,单位秒,如:1580000000\n ExpiredTime: data.expiredTime, // 临时密钥失效时间戳,是申请临时密钥时,时间戳加 durationSeconds\n ScopeLimit: this.scopeLimit, // 细粒度控制权限需要设为 true,会限制密钥只在相同请求时重复使用\n });\n })\n .catch((error) => {\n this.error('获取临时密钥失败:', error);\n callback(null);\n });\n },\n });\n this.cos = cos;\n this.log('COS SDK 初始化成功');\n }\n\n /**\n * 检查实例状态\n */\n protected checkState(methodName: string): boolean {\n if (this.isDestroyed) {\n this.warn(`实例已销毁,无法调用 ${methodName}`);\n return false;\n }\n if (!this.isInitialized) {\n this.warn(`实例未初始化,请先调用 init 方法`);\n return false;\n }\n return true;\n }\n\n /**\n * 内部日志方法\n */\n protected log(...args: unknown[]): void {\n if (this.debug) {\n console.log(`[${this.constructor.name}]`, ...args);\n }\n }\n\n protected warn(...args: unknown[]): void {\n if (this.debug) {\n console.warn(`[${this.constructor.name}]`, ...args);\n }\n }\n\n protected error(...args: unknown[]): void {\n if (this.debug) {\n console.error(`[${this.constructor.name}]`, ...args);\n }\n }\n\n /**\n * 验证配置\n */\n protected validateConfig(config: InitConfig): boolean {\n if (!config) {\n this.error('配置对象不能为空');\n return false;\n }\n\n if (!config.mode) {\n this.error('mode 是必需的');\n return false;\n }\n\n if (!['development', 'test', 'production'].includes(config.mode)) {\n this.error('mode 必须是 development、test 或 production');\n return false;\n }\n\n return true;\n }\n\n /**\n * 获取配置\n */\n getConfig(): { mode: EnvMode | null; headers: Record<string, any>; scopeLimit: boolean } | null {\n if (!this.isInitialized) {\n this.warn('实例未初始化');\n return null;\n }\n return {\n mode: this.mode,\n headers: this.headers,\n scopeLimit: this.scopeLimit,\n };\n }\n\n /**\n * 计算文件的 MD5 哈希值\n *\n * 性能优化:\n * - 小文件(< 10MB):一次性读取,速度更快\n * - 大文件:分片读取,避免内存溢出\n *\n * @param file - 要计算 MD5 的文件对象\n * @param chunkSize - 分片大小(字节),默认 2MB\n * @returns Promise<string> 文件的 MD5 哈希值\n *\n * @example\n * const md5 = await beLinkCOS.createFileMd5(file);\n * console.log('文件 MD5:', md5);\n */\n public createFileMd5(file: File, chunkSize: number = 2 * 1024 * 1024): Promise<string> {\n return new Promise((resolve, reject) => {\n // 对于小文件(< 10MB),使用简单方法\n if (file.size < 10 * 1024 * 1024) {\n const reader = new FileReader();\n\n reader.onload = (e: ProgressEvent<FileReader>) => {\n const arrayBuffer = e.target?.result as ArrayBuffer;\n if (arrayBuffer) {\n const wordArray = CryptoJS.lib.WordArray.create(arrayBuffer as any);\n const hash = CryptoJS.MD5(wordArray).toString();\n resolve(hash);\n } else {\n reject(new Error('Failed to read file'));\n }\n };\n\n reader.onerror = (error: ProgressEvent<FileReader>) => {\n reject(error);\n };\n\n reader.readAsArrayBuffer(file);\n return;\n }\n\n // 对于大文件,使用分片读取\n let currentChunk = 0;\n const chunks = Math.ceil(file.size / chunkSize);\n const spark = CryptoJS.algo.MD5.create();\n const reader = new FileReader();\n\n reader.onload = (e: ProgressEvent<FileReader>) => {\n const arrayBuffer = e.target?.result as ArrayBuffer;\n if (arrayBuffer) {\n const wordArray = CryptoJS.lib.WordArray.create(arrayBuffer as any);\n spark.update(wordArray);\n\n currentChunk++;\n\n if (currentChunk < chunks) {\n loadNext();\n } else {\n const hash = spark.finalize().toString();\n resolve(hash);\n }\n }\n };\n\n reader.onerror = (error: ProgressEvent<FileReader>) => {\n reject(error);\n };\n\n const loadNext = () => {\n const start = currentChunk * chunkSize;\n const end = Math.min(start + chunkSize, file.size);\n const blob = file.slice(start, end);\n reader.readAsArrayBuffer(blob);\n };\n\n loadNext();\n });\n }\n\n /**\n * 生成文件在 COS 中的存储路径\n *\n * 路径规则:/beLinkAllSource/static/{文件类型}/{文件名}_{时间戳}.{扩展名}\n * 例如:/beLinkAllSource/static/image/avatar_1699999999999.png\n *\n * @param file - 文件对象\n * @returns string COS 存储路径\n */\n private getFilePath(file: File): string {\n const filename = file.name;\n const fileType = file?.type ? file.type.split('/')[0] : 'other';\n const fileExtension = filename.split('.').pop() || '';\n const filenameDateNow = `${filename.split('.')[0]}_${Date.now()}`;\n const newFileName = `${filenameDateNow}.${fileExtension}`;\n return `${this.basePath}${fileType}/${newFileName}`;\n }\n\n /**\n * 获取文件上传后的访问 URL(不执行实际上传)\n *\n * 用于在上传前预览文件的最终 URL\n *\n * @param file - 文件对象\n * @returns Promise<string | null> 文件的完整访问 URL,如果实例未初始化则返回 null\n *\n * @example\n * const url = await beLinkCOS.getSourceUrl(file);\n * console.log('文件将上传到:', url);\n */\n public async getSourceUrl(file: File): Promise<string | null> {\n if (!this.checkState('getSourceUrl')) {\n return null;\n }\n\n const path = this.getFilePath(file);\n return `https://${BUCKETS_CONFIG[this.mode!].host}${path}`;\n }\n\n /**\n * 上传文件到腾讯云 COS\n *\n * 核心功能:将文件上传到对应环境的 COS 存储桶\n * 支持上传进度回调、自定义请求头等配置\n *\n * @param file - 要上传的文件对象\n * @param config - 上传配置(可选)\n * @param config.Headers - 自定义请求头\n * @param config.onProgressCallback - 上传进度回调函数\n * @returns Promise<UploadResult> 上传结果,包含文件 URL、ETag 等信息\n *\n * @example\n * const uploader = new BeLinkCOS({ mode: 'production' });\n * const result = await uploader.uploadFile(file, {\n * onProgressCallback: (progressData) => {\n * console.log('进度:', progressData.percent * 100 + '%');\n * }\n * });\n * console.log('文件 URL:', result.url);\n */\n public async uploadFile(file: File, config?: UploadConfig): Promise<UploadResult> {\n if (!this.checkState('uploadFile')) {\n throw new Error('实例未初始化或已销毁');\n }\n\n // 首次上传时自动初始化 COS SDK\n if (!this.cos) {\n await this.initCOS();\n }\n\n const key = this.getFilePath(file);\n this.log('开始上传文件:', { filename: file.name, key });\n\n return new Promise((resolve, reject) => {\n this.cos?.putObject(\n {\n Bucket: BUCKETS_CONFIG[this.mode!].name,\n Region: REGION,\n Key: key,\n Body: file,\n onProgress: config?.onProgressCallback,\n ...config,\n Headers: { ...this.headers, ...(config?.Headers || {}) },\n },\n (err: any, data: UploadResult) => {\n if (err) {\n this.error('上传失败:', err);\n reject(err);\n } else {\n const url = `https://${data.Location}`;\n this.log('上传成功:', url);\n resolve({ ...data, url });\n }\n },\n );\n });\n }\n\n /**\n * 销毁实例\n */\n destroy(): void {\n if (this.isDestroyed) {\n this.warn('实例已销毁');\n return;\n }\n\n try {\n // 清理 COS 实例\n this.cos = null;\n\n // 重置状态\n this.isDestroyed = true;\n this.isInitialized = false;\n this.mode = null;\n this.headers = {};\n this.scopeLimit = false;\n\n this.log('实例已销毁');\n } catch (error) {\n this.error('销毁实例失败:', error);\n }\n }\n}\n"],"names":[],"mappings":";;;AAoBA;;;AAGG;AACI,MAAM,cAAc,GAAkC;AAC3D,IAAA,WAAW,EAAE;AACX,QAAA,IAAI,EAAE,gBAAgB;AACtB,QAAA,IAAI,EAAE,4CAA4C;AAClD,QAAA,QAAQ,EAAE,OAAO;AAClB,KAAA;AACD,IAAA,IAAI,EAAE;AACJ,QAAA,IAAI,EAAE,gBAAgB;AACtB,QAAA,IAAI,EAAE,4CAA4C;AAClD,QAAA,QAAQ,EAAE,OAAO;AAClB,KAAA;AACD,IAAA,UAAU,EAAE;AACV,QAAA,IAAI,EAAE,oBAAoB;AAC1B,QAAA,IAAI,EAAE,sCAAsC;AAC5C,QAAA,QAAQ,EAAE,OAAO;AAClB,KAAA;;AAGH;AACO,MAAM,MAAM,GAAG;;ACpCtB;;;;;;;;;;;;;;;;;AAiBG;MACU,SAAS,CAAA;AAiBpB;;;;;;;;;;;;;;;;;;;;AAoBG;AACH,IAAA,WAAA,CAAY,MAAmB,EAAA;;QApCvB,IAAA,CAAA,GAAG,GAAQ,IAAI;;QAEN,IAAA,CAAA,QAAQ,GAAW,0BAA0B;;QAEvD,IAAA,CAAA,OAAO,GAAwB,EAAE;;QAEjC,IAAA,CAAA,IAAI,GAAmB,IAAI;;QAE1B,IAAA,CAAA,UAAU,GAAY,KAAK;;QAEzB,IAAA,CAAA,aAAa,GAAY,KAAK;QAC9B,IAAA,CAAA,WAAW,GAAY,KAAK;;QAE9B,IAAA,CAAA,KAAK,GAAY,KAAK;;QAyB5B,IAAI,MAAM,EAAE;AACV,YAAA,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC;QACnB;IACF;AAEA;;;;AAIG;AACH,IAAA,IAAI,CAAC,MAAkB,EAAA;AACrB,QAAA,IAAI,IAAI,CAAC,WAAW,EAAE;AACpB,YAAA,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC;YACxB;QACF;AAEA,QAAA,IAAI,IAAI,CAAC,aAAa,EAAE;AACtB,YAAA,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC;YAC1B;QACF;QAEA,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE;YAChC;QACF;;AAGA,QAAA,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI;QACvB,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,IAAI,EAAE;QACnC,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,IAAI,KAAK;QAC5C,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,KAAK;AAClC,QAAA,IAAI,CAAC,aAAa,GAAG,IAAI;AAEzB,QAAA,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;IAC9C;AAEA;;;;;;;AAOG;AACK,IAAA,MAAM,OAAO,GAAA;AACnB,QAAA,IAAI,IAAI,CAAC,GAAG,EAAE;AACZ,YAAA,OAAO;QACT;AAEA,QAAA,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC;AAClB,YAAA,gBAAgB,EAAE,CAAC,QAAa,EAAE,QAA0B,KAAI;AAC9D,gBAAA,MAAM,GAAG,GACP,IAAI,CAAC,IAAI,KAAK;AACZ,sBAAE;sBACA,oFAAoF;gBAC1F,KAAK,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE;qBAC1B,IAAI,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,IAAI,EAAE;AAClC,qBAAA,IAAI,CAAC,CAAC,MAAW,KAAI;AACpB,oBAAA,MAAM,IAAI,GAAG,MAAM,EAAE,IAAI,IAAI,EAAE;AAC/B,oBAAA,MAAM,WAAW,GAAG,IAAI,EAAE,WAAW;AAErC,oBAAA,QAAQ,CAAC;AACP,wBAAA,WAAW,EAAE,WAAW,CAAC,WAAW;AACpC,wBAAA,YAAY,EAAE,WAAW,CAAC,YAAY;AACtC,wBAAA,aAAa,EAAE,WAAW,CAAC,YAAY;;AAEvC,wBAAA,SAAS,EAAE,IAAI,CAAC,SAAS;AACzB,wBAAA,WAAW,EAAE,IAAI,CAAC,WAAW;AAC7B,wBAAA,UAAU,EAAE,IAAI,CAAC,UAAU;AAC5B,qBAAA,CAAC;AACJ,gBAAA,CAAC;AACA,qBAAA,KAAK,CAAC,CAAC,KAAK,KAAI;AACf,oBAAA,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC;oBAC9B,QAAQ,CAAC,IAAI,CAAC;AAChB,gBAAA,CAAC,CAAC;YACN,CAAC;AACF,SAAA,CAAC;AACF,QAAA,IAAI,CAAC,GAAG,GAAG,GAAG;AACd,QAAA,IAAI,CAAC,GAAG,CAAC,eAAe,CAAC;IAC3B;AAEA;;AAEG;AACO,IAAA,UAAU,CAAC,UAAkB,EAAA;AACrC,QAAA,IAAI,IAAI,CAAC,WAAW,EAAE;AACpB,YAAA,IAAI,CAAC,IAAI,CAAC,cAAc,UAAU,CAAA,CAAE,CAAC;AACrC,YAAA,OAAO,KAAK;QACd;AACA,QAAA,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE;AACvB,YAAA,IAAI,CAAC,IAAI,CAAC,CAAA,mBAAA,CAAqB,CAAC;AAChC,YAAA,OAAO,KAAK;QACd;AACA,QAAA,OAAO,IAAI;IACb;AAEA;;AAEG;IACO,GAAG,CAAC,GAAG,IAAe,EAAA;AAC9B,QAAA,IAAI,IAAI,CAAC,KAAK,EAAE;AACd,YAAA,OAAO,CAAC,GAAG,CAAC,CAAA,CAAA,EAAI,IAAI,CAAC,WAAW,CAAC,IAAI,CAAA,CAAA,CAAG,EAAE,GAAG,IAAI,CAAC;QACpD;IACF;IAEU,IAAI,CAAC,GAAG,IAAe,EAAA;AAC/B,QAAA,IAAI,IAAI,CAAC,KAAK,EAAE;AACd,YAAA,OAAO,CAAC,IAAI,CAAC,CAAA,CAAA,EAAI,IAAI,CAAC,WAAW,CAAC,IAAI,CAAA,CAAA,CAAG,EAAE,GAAG,IAAI,CAAC;QACrD;IACF;IAEU,KAAK,CAAC,GAAG,IAAe,EAAA;AAChC,QAAA,IAAI,IAAI,CAAC,KAAK,EAAE;AACd,YAAA,OAAO,CAAC,KAAK,CAAC,CAAA,CAAA,EAAI,IAAI,CAAC,WAAW,CAAC,IAAI,CAAA,CAAA,CAAG,EAAE,GAAG,IAAI,CAAC;QACtD;IACF;AAEA;;AAEG;AACO,IAAA,cAAc,CAAC,MAAkB,EAAA;QACzC,IAAI,CAAC,MAAM,EAAE;AACX,YAAA,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC;AACtB,YAAA,OAAO,KAAK;QACd;AAEA,QAAA,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE;AAChB,YAAA,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC;AACvB,YAAA,OAAO,KAAK;QACd;AAEA,QAAA,IAAI,CAAC,CAAC,aAAa,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE;AAChE,YAAA,IAAI,CAAC,KAAK,CAAC,wCAAwC,CAAC;AACpD,YAAA,OAAO,KAAK;QACd;AAEA,QAAA,OAAO,IAAI;IACb;AAEA;;AAEG;IACH,SAAS,GAAA;AACP,QAAA,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE;AACvB,YAAA,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC;AACnB,YAAA,OAAO,IAAI;QACb;QACA,OAAO;YACL,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,UAAU,EAAE,IAAI,CAAC,UAAU;SAC5B;IACH;AAEA;;;;;;;;;;;;;;AAcG;IACI,aAAa,CAAC,IAAU,EAAE,SAAA,GAAoB,CAAC,GAAG,IAAI,GAAG,IAAI,EAAA;QAClE,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,KAAI;;YAErC,IAAI,IAAI,CAAC,IAAI,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,EAAE;AAChC,gBAAA,MAAM,MAAM,GAAG,IAAI,UAAU,EAAE;AAE/B,gBAAA,MAAM,CAAC,MAAM,GAAG,CAAC,CAA4B,KAAI;AAC/C,oBAAA,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,EAAE,MAAqB;oBACnD,IAAI,WAAW,EAAE;AACf,wBAAA,MAAM,SAAS,GAAG,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,WAAkB,CAAC;wBACnE,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,QAAQ,EAAE;wBAC/C,OAAO,CAAC,IAAI,CAAC;oBACf;yBAAO;AACL,wBAAA,MAAM,CAAC,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;oBAC1C;AACF,gBAAA,CAAC;AAED,gBAAA,MAAM,CAAC,OAAO,GAAG,CAAC,KAAgC,KAAI;oBACpD,MAAM,CAAC,KAAK,CAAC;AACf,gBAAA,CAAC;AAED,gBAAA,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC;gBAC9B;YACF;;YAGA,IAAI,YAAY,GAAG,CAAC;AACpB,YAAA,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,SAAS,CAAC;YAC/C,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE;AACxC,YAAA,MAAM,MAAM,GAAG,IAAI,UAAU,EAAE;AAE/B,YAAA,MAAM,CAAC,MAAM,GAAG,CAAC,CAA4B,KAAI;AAC/C,gBAAA,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,EAAE,MAAqB;gBACnD,IAAI,WAAW,EAAE;AACf,oBAAA,MAAM,SAAS,GAAG,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,WAAkB,CAAC;AACnE,oBAAA,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC;AAEvB,oBAAA,YAAY,EAAE;AAEd,oBAAA,IAAI,YAAY,GAAG,MAAM,EAAE;AACzB,wBAAA,QAAQ,EAAE;oBACZ;yBAAO;wBACL,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;wBACxC,OAAO,CAAC,IAAI,CAAC;oBACf;gBACF;AACF,YAAA,CAAC;AAED,YAAA,MAAM,CAAC,OAAO,GAAG,CAAC,KAAgC,KAAI;gBACpD,MAAM,CAAC,KAAK,CAAC;AACf,YAAA,CAAC;YAED,MAAM,QAAQ,GAAG,MAAK;AACpB,gBAAA,MAAM,KAAK,GAAG,YAAY,GAAG,SAAS;AACtC,gBAAA,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC;gBAClD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC;AACnC,gBAAA,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC;AAChC,YAAA,CAAC;AAED,YAAA,QAAQ,EAAE;AACZ,QAAA,CAAC,CAAC;IACJ;AAEA;;;;;;;;AAQG;AACK,IAAA,WAAW,CAAC,IAAU,EAAA;AAC5B,QAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI;QAC1B,MAAM,QAAQ,GAAG,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO;AAC/D,QAAA,MAAM,aAAa,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE;AACrD,QAAA,MAAM,eAAe,GAAG,CAAA,EAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA,CAAA,EAAI,IAAI,CAAC,GAAG,EAAE,EAAE;AACjE,QAAA,MAAM,WAAW,GAAG,CAAA,EAAG,eAAe,CAAA,CAAA,EAAI,aAAa,EAAE;QACzD,OAAO,CAAA,EAAG,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAA,CAAA,EAAI,WAAW,CAAA,CAAE;IACrD;AAEA;;;;;;;;;;;AAWG;IACI,MAAM,YAAY,CAAC,IAAU,EAAA;QAClC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,EAAE;AACpC,YAAA,OAAO,IAAI;QACb;QAEA,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;AACnC,QAAA,OAAO,CAAA,QAAA,EAAW,cAAc,CAAC,IAAI,CAAC,IAAK,CAAC,CAAC,IAAI,CAAA,EAAG,IAAI,CAAA,CAAE;IAC5D;AAEA;;;;;;;;;;;;;;;;;;;;AAoBG;AACI,IAAA,MAAM,UAAU,CAAC,IAAU,EAAE,MAAqB,EAAA;QACvD,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE;AAClC,YAAA,MAAM,IAAI,KAAK,CAAC,YAAY,CAAC;QAC/B;;AAGA,QAAA,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE;AACb,YAAA,MAAM,IAAI,CAAC,OAAO,EAAE;QACtB;QAEA,MAAM,GAAG,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;AAClC,QAAA,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC;QAEjD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,KAAI;AACrC,YAAA,IAAI,CAAC,GAAG,EAAE,SAAS,CACjB;gBACE,MAAM,EAAE,cAAc,CAAC,IAAI,CAAC,IAAK,CAAC,CAAC,IAAI;AACvC,gBAAA,MAAM,EAAE,MAAM;AACd,gBAAA,GAAG,EAAE,GAAG;AACR,gBAAA,IAAI,EAAE,IAAI;gBACV,UAAU,EAAE,MAAM,EAAE,kBAAkB;AACtC,gBAAA,GAAG,MAAM;AACT,gBAAA,OAAO,EAAE,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,IAAI,MAAM,EAAE,OAAO,IAAI,EAAE,CAAC,EAAE;AACzD,aAAA,EACD,CAAC,GAAQ,EAAE,IAAkB,KAAI;gBAC/B,IAAI,GAAG,EAAE;AACP,oBAAA,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,CAAC;oBACxB,MAAM,CAAC,GAAG,CAAC;gBACb;qBAAO;AACL,oBAAA,MAAM,GAAG,GAAG,CAAA,QAAA,EAAW,IAAI,CAAC,QAAQ,EAAE;AACtC,oBAAA,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC;oBACtB,OAAO,CAAC,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,CAAC;gBAC3B;AACF,YAAA,CAAC,CACF;AACH,QAAA,CAAC,CAAC;IACJ;AAEA;;AAEG;IACH,OAAO,GAAA;AACL,QAAA,IAAI,IAAI,CAAC,WAAW,EAAE;AACpB,YAAA,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC;YAClB;QACF;AAEA,QAAA,IAAI;;AAEF,YAAA,IAAI,CAAC,GAAG,GAAG,IAAI;;AAGf,YAAA,IAAI,CAAC,WAAW,GAAG,IAAI;AACvB,YAAA,IAAI,CAAC,aAAa,GAAG,KAAK;AAC1B,YAAA,IAAI,CAAC,IAAI,GAAG,IAAI;AAChB,YAAA,IAAI,CAAC,OAAO,GAAG,EAAE;AACjB,YAAA,IAAI,CAAC,UAAU,GAAG,KAAK;AAEvB,YAAA,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC;QACnB;QAAE,OAAO,KAAK,EAAE;AACd,YAAA,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC;QAC9B;IACF;AACD;;;;"}
|