@cloudbase/manager-node 5.8.7 → 5.8.8-beta.0
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/lib/env/index.js +9 -28
- package/lib/hosting/index.js +53 -32
- package/lib/storage/index.js +135 -99
- package/lib/utils/storage-config.js +73 -0
- package/package.json +1 -1
- package/types/env/index.d.ts +7 -16
- package/types/env/type.d.ts +0 -56
- package/types/hosting/index.d.ts +8 -1
- package/types/interfaces/tcb.interface.d.ts +1 -0
- package/types/storage/index.d.ts +20 -2
- package/types/storage/type.d.ts +2 -0
- package/types/utils/storage-config.d.ts +36 -0
package/lib/storage/index.js
CHANGED
|
@@ -22,6 +22,7 @@ const cos_nodejs_sdk_v5_1 = __importDefault(require("cos-nodejs-sdk-v5"));
|
|
|
22
22
|
const utils_1 = require("../utils");
|
|
23
23
|
const error_1 = require("../error");
|
|
24
24
|
const parallel_1 = require("../utils/parallel");
|
|
25
|
+
const storage_config_1 = require("../utils/storage-config");
|
|
25
26
|
const runenv_1 = require("../utils/runenv");
|
|
26
27
|
const constant_1 = require("../constant");
|
|
27
28
|
const helper_1 = require("./helper");
|
|
@@ -44,15 +45,17 @@ class StorageService {
|
|
|
44
45
|
* 过滤目录占位对象。prefix 无前导斜杠,空串表示整桶(Hosting 桶请用 getRemoteManifestCustom)。
|
|
45
46
|
*/
|
|
46
47
|
async getRemoteManifest(prefix) {
|
|
47
|
-
const { bucket, region } = this.getStorageConfig();
|
|
48
|
+
const { bucket, region, basePath } = this.getStorageConfig();
|
|
48
49
|
return this.getRemoteManifestCustom({
|
|
49
50
|
prefix,
|
|
50
51
|
bucket,
|
|
51
|
-
region
|
|
52
|
+
region,
|
|
53
|
+
basePath
|
|
52
54
|
});
|
|
53
55
|
}
|
|
54
56
|
/**
|
|
55
57
|
* 获取远端 manifest,支持自定义 Bucket 和 Region。prefix 规则同 getRemoteManifest。
|
|
58
|
+
* 不会自动拼接环境的共享桶 BasePath,需要时显式传入 basePath
|
|
56
59
|
*/
|
|
57
60
|
async getRemoteManifestCustom(options) {
|
|
58
61
|
const fileList = await this.walkCloudDirCustom(options);
|
|
@@ -162,13 +165,14 @@ class StorageService {
|
|
|
162
165
|
* 始终跳过 .cloudbase-backup/ 下的历史备份,避免根目录发布时递归备份。
|
|
163
166
|
*/
|
|
164
167
|
async backupRemote(options) {
|
|
165
|
-
const { bucket, region, prefix, backupPrefix, parallel = 20 } = options;
|
|
168
|
+
const { bucket, region, prefix, backupPrefix, parallel = 20, basePath = '' } = options;
|
|
166
169
|
// COS 对象 Key 不保留前导斜杠,统一规范化为 .cloudbase-backup/... 形式
|
|
167
170
|
const normalizedBackupPrefix = backupPrefix.replace(/^\/+/, '').replace(/\/+$/, '');
|
|
168
171
|
const remoteManifest = await this.getRemoteManifestCustom({
|
|
169
172
|
bucket,
|
|
170
173
|
region,
|
|
171
|
-
prefix
|
|
174
|
+
prefix,
|
|
175
|
+
basePath
|
|
172
176
|
});
|
|
173
177
|
// 过滤掉已备份的文件,避免根目录发布时递归备份旧备份,filesToBackup 存的是需要备份的文件
|
|
174
178
|
const filesToBackup = remoteManifest.filter(item => !item.key.replace(/^\/+/, '').startsWith(CONSISTENT_DEPLOY_BACKUP_PREFIX));
|
|
@@ -183,11 +187,11 @@ class StorageService {
|
|
|
183
187
|
filesToBackup.map(file => async () => {
|
|
184
188
|
const destPath = `${normalizedBackupPrefix}/${file.key.replace(/^\/+/, '')}`;
|
|
185
189
|
// 拼接复制源
|
|
186
|
-
const copySource = `${bucket}.cos.${region}.myqcloud.com/${encodeURIComponent(file.key)}`;
|
|
190
|
+
const copySource = `${bucket}.cos.${region}.myqcloud.com/${encodeURIComponent((0, storage_config_1.toPhysicalKey)(basePath, file.key))}`;
|
|
187
191
|
const res = await putObjectCopy({
|
|
188
192
|
Bucket: bucket,
|
|
189
193
|
Region: region,
|
|
190
|
-
Key: destPath,
|
|
194
|
+
Key: (0, storage_config_1.toPhysicalKey)(basePath, destPath),
|
|
191
195
|
CopySource: copySource
|
|
192
196
|
});
|
|
193
197
|
if (res.statusCode !== 200) {
|
|
@@ -207,7 +211,7 @@ class StorageService {
|
|
|
207
211
|
* 任一阶段失败都会抛错,由上层决定是否保留备份供人工恢复。
|
|
208
212
|
*/
|
|
209
213
|
async restoreFromBackup(options) {
|
|
210
|
-
const { bucket, region, backupPrefix, overwrittenKeys, addedKeys, parallel = 20 } = options;
|
|
214
|
+
const { bucket, region, backupPrefix, overwrittenKeys, addedKeys, parallel = 20, basePath = '' } = options;
|
|
211
215
|
// COS 对象 Key 不保留前导斜杠,与 backupRemote 的规范化规则保持一致
|
|
212
216
|
const normalizedBackupPrefix = backupPrefix.replace(/^\/+/, '').replace(/\/+$/, '');
|
|
213
217
|
if (overwrittenKeys.length > 0) {
|
|
@@ -216,11 +220,11 @@ class StorageService {
|
|
|
216
220
|
const controller = new parallel_1.AsyncTaskParallelController(parallel, 50);
|
|
217
221
|
controller.loadTasks(overwrittenKeys.map(key => async () => {
|
|
218
222
|
const sourcePath = `${normalizedBackupPrefix}/${key.replace(/^\/+/, '')}`;
|
|
219
|
-
const copySource = `${bucket}.cos.${region}.myqcloud.com/${encodeURIComponent(sourcePath)}`;
|
|
223
|
+
const copySource = `${bucket}.cos.${region}.myqcloud.com/${encodeURIComponent((0, storage_config_1.toPhysicalKey)(basePath, sourcePath))}`;
|
|
220
224
|
const res = await putObjectCopy({
|
|
221
225
|
Bucket: bucket,
|
|
222
226
|
Region: region,
|
|
223
|
-
Key: key,
|
|
227
|
+
Key: (0, storage_config_1.toPhysicalKey)(basePath, key),
|
|
224
228
|
CopySource: copySource
|
|
225
229
|
});
|
|
226
230
|
if (res.statusCode !== 200) {
|
|
@@ -236,7 +240,7 @@ class StorageService {
|
|
|
236
240
|
}
|
|
237
241
|
}
|
|
238
242
|
if (addedKeys.length > 0) {
|
|
239
|
-
await this.deleteFileCustom(addedKeys, bucket, region);
|
|
243
|
+
await this.deleteFileCustom(addedKeys.map(key => (0, storage_config_1.toPhysicalKey)(basePath, key)), bucket, region);
|
|
240
244
|
}
|
|
241
245
|
}
|
|
242
246
|
/**
|
|
@@ -244,14 +248,15 @@ class StorageService {
|
|
|
244
248
|
* 备份目录以 . 开头,内部使用空 Marker 列举,不受默认排除规则影响。
|
|
245
249
|
*/
|
|
246
250
|
async cleanupBackup(options) {
|
|
247
|
-
const { bucket, region, backupPrefix } = options;
|
|
251
|
+
const { bucket, region, backupPrefix, basePath } = options;
|
|
248
252
|
// 备份目录以 . 开头,会被 walkCloudDirCustom 默认 marker='/' 排除,
|
|
249
253
|
// 必须显式传 marker='' 才能列举到备份对象
|
|
250
254
|
await this.deleteDirectoryCustom({
|
|
251
255
|
cloudPath: backupPrefix,
|
|
252
256
|
bucket,
|
|
253
257
|
region,
|
|
254
|
-
marker: ''
|
|
258
|
+
marker: '',
|
|
259
|
+
basePath
|
|
255
260
|
});
|
|
256
261
|
}
|
|
257
262
|
/**
|
|
@@ -260,7 +265,7 @@ class StorageService {
|
|
|
260
265
|
* 单个删除失败记入 failedKeys,不影响其他文件。
|
|
261
266
|
*/
|
|
262
267
|
async pruneRemote(options) {
|
|
263
|
-
const { bucket, region, prefix, removedKeys, parallel = 20 } = options;
|
|
268
|
+
const { bucket, region, prefix, removedKeys, parallel = 20, basePath = '' } = options;
|
|
264
269
|
const normalizedPrefix = prefix.replace(/^\/+/, '').replace(/\/+$/, '');
|
|
265
270
|
const prefixWithSlash = normalizedPrefix ? `${normalizedPrefix}/` : '';
|
|
266
271
|
const backupPrefix = CONSISTENT_DEPLOY_BACKUP_PREFIX;
|
|
@@ -281,7 +286,7 @@ class StorageService {
|
|
|
281
286
|
await deleteObject({
|
|
282
287
|
Bucket: bucket,
|
|
283
288
|
Region: region,
|
|
284
|
-
Key: key
|
|
289
|
+
Key: (0, storage_config_1.toPhysicalKey)(basePath, key)
|
|
285
290
|
});
|
|
286
291
|
return { key };
|
|
287
292
|
}
|
|
@@ -333,7 +338,7 @@ class StorageService {
|
|
|
333
338
|
* cleanupBackup 显式清理备份。调用方(CLI)仍须在开启 prune 前要求用户二次确认。
|
|
334
339
|
*/
|
|
335
340
|
async uploadDirectoryConsistent(options) {
|
|
336
|
-
const { localPath, cloudPath = '', bucket, region, ignore, fileId = true, parallel = 20, retryCount = 0, retryInterval = 500, onProgress, onFileFinish, verify = false, safe = false, prune = false, entries } = options;
|
|
341
|
+
const { localPath, cloudPath = '', bucket, region, ignore, fileId = true, parallel = 20, retryCount = 0, retryInterval = 500, onProgress, onFileFinish, verify = false, safe = false, prune = false, entries, basePath } = options;
|
|
337
342
|
const resolvePath = path_1.default.resolve(localPath);
|
|
338
343
|
const resolveLocalPath = path_1.default.join(resolvePath, path_1.default.sep);
|
|
339
344
|
const filePaths = await this.walkLocalDir(resolveLocalPath, ignore);
|
|
@@ -350,7 +355,8 @@ class StorageService {
|
|
|
350
355
|
const remoteSnapshot = await this.getRemoteManifestCustom({
|
|
351
356
|
bucket,
|
|
352
357
|
region,
|
|
353
|
-
prefix: cloudPath
|
|
358
|
+
prefix: cloudPath,
|
|
359
|
+
basePath
|
|
354
360
|
});
|
|
355
361
|
// 发布前快照同时用于 safe 回滚判定和 prune 待删文件判定。
|
|
356
362
|
diff = this.diffManifest(localManifest, remoteSnapshot);
|
|
@@ -364,7 +370,8 @@ class StorageService {
|
|
|
364
370
|
region,
|
|
365
371
|
prefix: cloudPath,
|
|
366
372
|
backupPrefix,
|
|
367
|
-
parallel
|
|
373
|
+
parallel,
|
|
374
|
+
basePath
|
|
368
375
|
});
|
|
369
376
|
}
|
|
370
377
|
try {
|
|
@@ -374,6 +381,7 @@ class StorageService {
|
|
|
374
381
|
entryFiles: entries,
|
|
375
382
|
bucket,
|
|
376
383
|
region,
|
|
384
|
+
basePath,
|
|
377
385
|
ignore,
|
|
378
386
|
fileId,
|
|
379
387
|
parallel,
|
|
@@ -387,7 +395,8 @@ class StorageService {
|
|
|
387
395
|
const remoteManifest = await this.getRemoteManifestCustom({
|
|
388
396
|
bucket,
|
|
389
397
|
region,
|
|
390
|
-
prefix: cloudPath
|
|
398
|
+
prefix: cloudPath,
|
|
399
|
+
basePath
|
|
391
400
|
});
|
|
392
401
|
const verifyResult = this.verifyManifest(localManifest, remoteManifest);
|
|
393
402
|
if (!verifyResult.ok) {
|
|
@@ -404,6 +413,7 @@ class StorageService {
|
|
|
404
413
|
await this.restoreFromBackup({
|
|
405
414
|
bucket,
|
|
406
415
|
region,
|
|
416
|
+
basePath,
|
|
407
417
|
backupPrefix,
|
|
408
418
|
overwrittenKeys: diff.overwritten,
|
|
409
419
|
addedKeys: diff.added,
|
|
@@ -421,6 +431,7 @@ class StorageService {
|
|
|
421
431
|
? await this.pruneRemote({
|
|
422
432
|
bucket,
|
|
423
433
|
region,
|
|
434
|
+
basePath,
|
|
424
435
|
prefix: cloudPath,
|
|
425
436
|
removedKeys: (diff === null || diff === void 0 ? void 0 : diff.removed) || [],
|
|
426
437
|
parallel
|
|
@@ -445,12 +456,13 @@ class StorageService {
|
|
|
445
456
|
*/
|
|
446
457
|
async uploadFile(options) {
|
|
447
458
|
const { localPath, cloudPath = '', onProgress } = options;
|
|
448
|
-
const { bucket, region } = this.getStorageConfig();
|
|
459
|
+
const { bucket, region, basePath } = this.getStorageConfig();
|
|
449
460
|
return this.uploadFileCustom({
|
|
450
461
|
localPath,
|
|
451
462
|
cloudPath,
|
|
452
463
|
bucket,
|
|
453
464
|
region,
|
|
465
|
+
basePath,
|
|
454
466
|
onProgress
|
|
455
467
|
});
|
|
456
468
|
}
|
|
@@ -460,11 +472,12 @@ class StorageService {
|
|
|
460
472
|
*/
|
|
461
473
|
async uploadFiles(options) {
|
|
462
474
|
const { files, onProgress, parallel, onFileFinish, ignore, retryCount, retryInterval } = options;
|
|
463
|
-
const { bucket, region } = this.getStorageConfig();
|
|
475
|
+
const { bucket, region, basePath } = this.getStorageConfig();
|
|
464
476
|
return this.uploadFilesCustom({
|
|
465
477
|
files,
|
|
466
478
|
bucket,
|
|
467
479
|
region,
|
|
480
|
+
basePath,
|
|
468
481
|
ignore,
|
|
469
482
|
parallel,
|
|
470
483
|
onProgress,
|
|
@@ -475,13 +488,14 @@ class StorageService {
|
|
|
475
488
|
}
|
|
476
489
|
/**
|
|
477
490
|
* 上传文件,支持自定义 Bucket 和 Region
|
|
491
|
+
* 不会自动拼接环境的共享桶 BasePath,需要时显式传入 basePath;fileId 始终按逻辑路径 cloudPath 获取
|
|
478
492
|
* @param {string} localPath
|
|
479
493
|
* @param {string} cloudPath
|
|
480
494
|
* @param {string} bucket
|
|
481
495
|
* @param {string} region
|
|
482
496
|
*/
|
|
483
497
|
async uploadFileCustom(options) {
|
|
484
|
-
const { localPath, cloudPath, bucket, region, onProgress, fileId = true } = options;
|
|
498
|
+
const { localPath, cloudPath, bucket, region, onProgress, fileId = true, basePath = '' } = options;
|
|
485
499
|
let localFilePath = '';
|
|
486
500
|
let resolveLocalPath = path_1.default.resolve(localPath);
|
|
487
501
|
(0, utils_1.checkFullAccess)(resolveLocalPath, true);
|
|
@@ -517,7 +531,7 @@ class StorageService {
|
|
|
517
531
|
onProgress,
|
|
518
532
|
Bucket: bucket,
|
|
519
533
|
Region: region,
|
|
520
|
-
Key: cloudPath,
|
|
534
|
+
Key: (0, storage_config_1.toPhysicalKey)(basePath, cloudPath),
|
|
521
535
|
StorageClass: 'STANDARD',
|
|
522
536
|
ContentLength: fileStats.size,
|
|
523
537
|
Body: fs_1.default.createReadStream(localFilePath),
|
|
@@ -529,7 +543,7 @@ class StorageService {
|
|
|
529
543
|
res = await sliceUploadFile({
|
|
530
544
|
Bucket: bucket,
|
|
531
545
|
Region: region,
|
|
532
|
-
Key: cloudPath,
|
|
546
|
+
Key: (0, storage_config_1.toPhysicalKey)(basePath, cloudPath),
|
|
533
547
|
FilePath: localFilePath,
|
|
534
548
|
StorageClass: 'STANDARD',
|
|
535
549
|
AsyncLimit: 3,
|
|
@@ -557,7 +571,7 @@ class StorageService {
|
|
|
557
571
|
const { localPath, cloudPath = '', ignore, onProgress, onFileFinish, parallel, retryCount, retryInterval } = options;
|
|
558
572
|
// 此处不检查路径是否存在
|
|
559
573
|
// 绝对路径 /var/blog/xxxx
|
|
560
|
-
const { bucket, region } = this.getStorageConfig();
|
|
574
|
+
const { bucket, region, basePath } = this.getStorageConfig();
|
|
561
575
|
return this.uploadDirectoryCustom({
|
|
562
576
|
localPath,
|
|
563
577
|
cloudPath,
|
|
@@ -566,6 +580,7 @@ class StorageService {
|
|
|
566
580
|
retryInterval,
|
|
567
581
|
bucket,
|
|
568
582
|
region,
|
|
583
|
+
basePath,
|
|
569
584
|
ignore,
|
|
570
585
|
onProgress,
|
|
571
586
|
onFileFinish
|
|
@@ -573,6 +588,7 @@ class StorageService {
|
|
|
573
588
|
}
|
|
574
589
|
/**
|
|
575
590
|
* 上传文件夹,支持自定义 Region 和 Bucket
|
|
591
|
+
* 不会自动拼接环境的共享桶 BasePath,需要时显式传入 basePath
|
|
576
592
|
* @param {string} localPath
|
|
577
593
|
* @param {string} cloudPath
|
|
578
594
|
* @param {number} parallel
|
|
@@ -584,7 +600,7 @@ class StorageService {
|
|
|
584
600
|
* @returns {Promise<void>}
|
|
585
601
|
*/
|
|
586
602
|
async uploadDirectoryCustom(options) {
|
|
587
|
-
const { localPath, cloudPath, entryFiles, bucket, region, onProgress, onFileFinish, ignore, fileId = true, parallel = 20, retryCount = 0, retryInterval = 500 } = options;
|
|
603
|
+
const { localPath, cloudPath, entryFiles, bucket, region, basePath, onProgress, onFileFinish, ignore, fileId = true, parallel = 20, retryCount = 0, retryInterval = 500 } = options;
|
|
588
604
|
// 此处不检查路径是否存在
|
|
589
605
|
// 绝对路径 /var/blog/xxxx
|
|
590
606
|
const resolvePath = path_1.default.resolve(localPath);
|
|
@@ -622,7 +638,8 @@ class StorageService {
|
|
|
622
638
|
.map(info => () => this.createCloudDirectroyCustom({
|
|
623
639
|
cloudPath: info.cloudFileKey,
|
|
624
640
|
bucket,
|
|
625
|
-
region
|
|
641
|
+
region,
|
|
642
|
+
basePath
|
|
626
643
|
}));
|
|
627
644
|
creatingDirController.loadTasks(creatingDirTasks);
|
|
628
645
|
await creatingDirController.run();
|
|
@@ -664,6 +681,7 @@ class StorageService {
|
|
|
664
681
|
files: resourceFiles,
|
|
665
682
|
bucket,
|
|
666
683
|
region,
|
|
684
|
+
basePath,
|
|
667
685
|
ignore: [],
|
|
668
686
|
onProgress,
|
|
669
687
|
onFileFinish,
|
|
@@ -677,6 +695,7 @@ class StorageService {
|
|
|
677
695
|
files: entryUploadFiles,
|
|
678
696
|
bucket,
|
|
679
697
|
region,
|
|
698
|
+
basePath,
|
|
680
699
|
ignore: [],
|
|
681
700
|
onProgress,
|
|
682
701
|
onFileFinish,
|
|
@@ -688,7 +707,7 @@ class StorageService {
|
|
|
688
707
|
}
|
|
689
708
|
async uploadFilesCustom(options) {
|
|
690
709
|
var _a;
|
|
691
|
-
const { files, bucket, region, ignore, onProgress, onFileFinish, fileId = true, parallel = 20, retryCount = 0, retryInterval = 500 } = options;
|
|
710
|
+
const { files, bucket, region, basePath = '', ignore, onProgress, onFileFinish, fileId = true, parallel = 20, retryCount = 0, retryInterval = 500 } = options;
|
|
692
711
|
if (!files || !files.length) {
|
|
693
712
|
return;
|
|
694
713
|
}
|
|
@@ -710,6 +729,7 @@ class StorageService {
|
|
|
710
729
|
fileList,
|
|
711
730
|
bucket,
|
|
712
731
|
region,
|
|
732
|
+
basePath,
|
|
713
733
|
fileId,
|
|
714
734
|
retryCount,
|
|
715
735
|
retryInterval,
|
|
@@ -734,6 +754,7 @@ class StorageService {
|
|
|
734
754
|
fileList,
|
|
735
755
|
bucket,
|
|
736
756
|
region,
|
|
757
|
+
basePath,
|
|
737
758
|
fileId,
|
|
738
759
|
parallel,
|
|
739
760
|
retryCount: 0, // 并行阶段不重试,失败后交给串行阶段
|
|
@@ -747,6 +768,7 @@ class StorageService {
|
|
|
747
768
|
fileList: parallelFailedFiles,
|
|
748
769
|
bucket,
|
|
749
770
|
region,
|
|
771
|
+
basePath,
|
|
750
772
|
fileId: false, // 串行重试不需要再获取 fileId
|
|
751
773
|
retryCount,
|
|
752
774
|
retryInterval,
|
|
@@ -774,7 +796,7 @@ class StorageService {
|
|
|
774
796
|
*/
|
|
775
797
|
// eslint-disable-next-line @typescript-eslint/member-ordering
|
|
776
798
|
async uploadFilesSerial(options) {
|
|
777
|
-
const { fileList, bucket, region, fileId, retryCount, retryInterval, onFileFinish } = options;
|
|
799
|
+
const { fileList, bucket, region, basePath, fileId, retryCount, retryInterval, onFileFinish } = options;
|
|
778
800
|
let uploadedCount = 0;
|
|
779
801
|
let failedFiles = [];
|
|
780
802
|
for (let i = 0; i < fileList.length; i++) {
|
|
@@ -789,7 +811,7 @@ class StorageService {
|
|
|
789
811
|
await putObject({
|
|
790
812
|
Bucket: bucket,
|
|
791
813
|
Region: region,
|
|
792
|
-
Key: file.cloudFileKey,
|
|
814
|
+
Key: (0, storage_config_1.toPhysicalKey)(basePath, file.cloudFileKey),
|
|
793
815
|
Body: fs_1.default.createReadStream(file.filePath),
|
|
794
816
|
ContentLength: fs_1.default.statSync(file.filePath).size,
|
|
795
817
|
});
|
|
@@ -840,7 +862,7 @@ class StorageService {
|
|
|
840
862
|
*/
|
|
841
863
|
// eslint-disable-next-line @typescript-eslint/member-ordering
|
|
842
864
|
async uploadFilesParallel(options) {
|
|
843
|
-
const { fileList, bucket, region, fileId, parallel, retryCount, retryInterval, onProgress, onFileFinish } = options;
|
|
865
|
+
const { fileList, bucket, region, basePath, fileId, parallel, retryCount, retryInterval, onProgress, onFileFinish } = options;
|
|
844
866
|
// 生成上传文件属性
|
|
845
867
|
const tasks = fileList.map(stats => async () => {
|
|
846
868
|
let cosFileId;
|
|
@@ -851,7 +873,7 @@ class StorageService {
|
|
|
851
873
|
return {
|
|
852
874
|
Bucket: bucket,
|
|
853
875
|
Region: region,
|
|
854
|
-
Key: stats.cloudFileKey,
|
|
876
|
+
Key: (0, storage_config_1.toPhysicalKey)(basePath, stats.cloudFileKey),
|
|
855
877
|
FilePath: stats.filePath,
|
|
856
878
|
'x-cos-meta-fileid': cosFileId
|
|
857
879
|
};
|
|
@@ -887,7 +909,8 @@ class StorageService {
|
|
|
887
909
|
console.warn('[parallel-upload] onFileFinish received null/undefined file');
|
|
888
910
|
return;
|
|
889
911
|
}
|
|
890
|
-
|
|
912
|
+
// file.Key 是 COS 物理 Key,回调与失败重试统一使用逻辑路径
|
|
913
|
+
const fileKey = (0, storage_config_1.toLogicalPath)(basePath, file.Key || file.cloudFileKey);
|
|
891
914
|
if (error) {
|
|
892
915
|
batchFailedFiles.push({
|
|
893
916
|
filePath: file.FilePath || file.filePath,
|
|
@@ -900,7 +923,9 @@ class StorageService {
|
|
|
900
923
|
}
|
|
901
924
|
// 继续调用用户传入的回调(转换参数格式)
|
|
902
925
|
// 注意:cloudFileKey 和 filePath 是 CLI 期望的格式
|
|
903
|
-
onFileFinish === null || onFileFinish === void 0 ? void 0 : onFileFinish(error, result, Object.assign(Object.assign({}, file), {
|
|
926
|
+
onFileFinish === null || onFileFinish === void 0 ? void 0 : onFileFinish(error, result, Object.assign(Object.assign({}, file), {
|
|
927
|
+
// Key 同样返回逻辑路径,避免调用方(如 tcb storage upload)展示带 BasePath 的物理 Key
|
|
928
|
+
Key: fileKey, cloudFileKey: fileKey, filePath: file.FilePath || file.filePath }));
|
|
904
929
|
}
|
|
905
930
|
};
|
|
906
931
|
try {
|
|
@@ -921,7 +946,7 @@ class StorageService {
|
|
|
921
946
|
// onFileFinish 未被调用,整个批次失败
|
|
922
947
|
allFailedFiles.push(...batchFiles.map(f => ({
|
|
923
948
|
filePath: f.FilePath || f.filePath,
|
|
924
|
-
cloudFileKey: f.Key || f.cloudFileKey,
|
|
949
|
+
cloudFileKey: (0, storage_config_1.toLogicalPath)(basePath, f.Key || f.cloudFileKey),
|
|
925
950
|
error: error.message || String(error)
|
|
926
951
|
})));
|
|
927
952
|
}
|
|
@@ -945,24 +970,26 @@ class StorageService {
|
|
|
945
970
|
* @param {string} cloudPath
|
|
946
971
|
*/
|
|
947
972
|
async createCloudDirectroy(cloudPath) {
|
|
948
|
-
const { bucket, region } = this.getStorageConfig();
|
|
973
|
+
const { bucket, region, basePath } = this.getStorageConfig();
|
|
949
974
|
await this.createCloudDirectroyCustom({
|
|
950
975
|
cloudPath,
|
|
951
976
|
bucket,
|
|
952
|
-
region
|
|
977
|
+
region,
|
|
978
|
+
basePath
|
|
953
979
|
});
|
|
954
980
|
}
|
|
955
981
|
/**
|
|
956
982
|
* 创建一个空的文件夹,支持自定义 Region 和 Bucket
|
|
983
|
+
* 不会自动拼接环境的共享桶 BasePath,需要时显式传入 basePath
|
|
957
984
|
* @param {string} cloudPath
|
|
958
985
|
* @param {string} bucket
|
|
959
986
|
* @param {string} region
|
|
960
987
|
*/
|
|
961
988
|
async createCloudDirectroyCustom(options) {
|
|
962
|
-
const { cloudPath, bucket, region } = options;
|
|
989
|
+
const { cloudPath, bucket, region, basePath = '' } = options;
|
|
963
990
|
const cos = this.getCos();
|
|
964
991
|
const putObject = util_1.default.promisify(cos.putObject).bind(cos);
|
|
965
|
-
const dirKey = this.getCloudKey(cloudPath);
|
|
992
|
+
const dirKey = (0, storage_config_1.toPhysicalKey)(basePath, this.getCloudKey(cloudPath));
|
|
966
993
|
const res = await putObject({
|
|
967
994
|
Bucket: bucket,
|
|
968
995
|
Region: region,
|
|
@@ -1134,6 +1161,7 @@ class StorageService {
|
|
|
1134
1161
|
}
|
|
1135
1162
|
/**
|
|
1136
1163
|
* 删除文件,可以指定 Bucket 和 Region
|
|
1164
|
+
* 不会自动拼接环境的共享桶 BasePath,cloudPathList 按 COS 原始 Key 处理
|
|
1137
1165
|
* @param {string[]} cloudPathList
|
|
1138
1166
|
* @param {string} bucket
|
|
1139
1167
|
* @param {string} region
|
|
@@ -1164,11 +1192,11 @@ class StorageService {
|
|
|
1164
1192
|
async getFileInfo(cloudPath) {
|
|
1165
1193
|
const cos = this.getCos();
|
|
1166
1194
|
const headObject = util_1.default.promisify(cos.headObject).bind(cos);
|
|
1167
|
-
const { bucket, region } = this.getStorageConfig();
|
|
1195
|
+
const { bucket, region, basePath } = this.getStorageConfig();
|
|
1168
1196
|
const { headers } = await headObject({
|
|
1169
1197
|
Bucket: bucket,
|
|
1170
1198
|
Region: region,
|
|
1171
|
-
Key: cloudPath
|
|
1199
|
+
Key: (0, storage_config_1.toPhysicalKey)(basePath, cloudPath)
|
|
1172
1200
|
});
|
|
1173
1201
|
if (!headers) {
|
|
1174
1202
|
throw new error_1.CloudBaseError(`[${cloudPath}] 获取文件信息失败`);
|
|
@@ -1188,31 +1216,34 @@ class StorageService {
|
|
|
1188
1216
|
* @returns {Promise<void>}
|
|
1189
1217
|
*/
|
|
1190
1218
|
async deleteDirectory(cloudPath) {
|
|
1191
|
-
const { bucket, region } = this.getStorageConfig();
|
|
1219
|
+
const { bucket, region, basePath } = this.getStorageConfig();
|
|
1192
1220
|
return this.deleteDirectoryCustom({
|
|
1193
1221
|
cloudPath,
|
|
1194
1222
|
bucket,
|
|
1195
|
-
region
|
|
1223
|
+
region,
|
|
1224
|
+
basePath
|
|
1196
1225
|
});
|
|
1197
1226
|
}
|
|
1198
1227
|
/**
|
|
1199
1228
|
* 删除文件,可以指定 bucket 和 region
|
|
1229
|
+
* 不会自动拼接环境的共享桶 BasePath,需要时显式传入 basePath;返回的 Key 为逻辑路径
|
|
1200
1230
|
* @param {string} cloudPath
|
|
1201
1231
|
* @param {string} bucket
|
|
1202
1232
|
* @param {string} region
|
|
1203
1233
|
* @returns {Promise<void>}
|
|
1204
1234
|
*/
|
|
1205
1235
|
async deleteDirectoryCustom(options) {
|
|
1206
|
-
const { cloudPath, bucket, region, marker } = options;
|
|
1236
|
+
const { cloudPath, bucket, region, marker, basePath = '' } = options;
|
|
1207
1237
|
const key = this.getCloudKey(cloudPath);
|
|
1208
1238
|
const cos = this.getCos();
|
|
1209
1239
|
const deleteMultipleObject = util_1.default.promisify(cos.deleteMultipleObject).bind(cos);
|
|
1210
|
-
//
|
|
1240
|
+
// 遍历获取全部文件(返回的 Key 为逻辑路径)
|
|
1211
1241
|
const files = await this.walkCloudDirCustom({
|
|
1212
1242
|
bucket,
|
|
1213
1243
|
region,
|
|
1214
1244
|
prefix: key,
|
|
1215
|
-
marker
|
|
1245
|
+
marker,
|
|
1246
|
+
basePath
|
|
1216
1247
|
});
|
|
1217
1248
|
// 文件为空时,不能调用删除接口
|
|
1218
1249
|
if (!files.length) {
|
|
@@ -1231,16 +1262,17 @@ class StorageService {
|
|
|
1231
1262
|
const tasks = sliceGroup.map(group => deleteMultipleObject({
|
|
1232
1263
|
Bucket: bucket,
|
|
1233
1264
|
Region: region,
|
|
1234
|
-
Objects: group.map(file => ({ Key: file.Key }))
|
|
1265
|
+
Objects: group.map(file => ({ Key: (0, storage_config_1.toPhysicalKey)(basePath, file.Key) }))
|
|
1235
1266
|
}));
|
|
1236
1267
|
// 删除多个文件
|
|
1237
1268
|
const taskRes = await Promise.all(tasks);
|
|
1238
|
-
//
|
|
1269
|
+
// 合并响应结果,Key 映射回逻辑路径
|
|
1270
|
+
const toLogicalItems = (items) => basePath ? items.map(item => (Object.assign(Object.assign({}, item), { Key: (0, storage_config_1.toLogicalPath)(basePath, item.Key) }))) : items;
|
|
1239
1271
|
const Deleted = taskRes.map(_ => _.Deleted).reduce((prev, next) => [...prev, ...next], []);
|
|
1240
1272
|
const Error = taskRes.map(_ => _.Error).reduce((prev, next) => [...prev, ...next], []);
|
|
1241
1273
|
return {
|
|
1242
|
-
Deleted,
|
|
1243
|
-
Error
|
|
1274
|
+
Deleted: toLogicalItems(Deleted),
|
|
1275
|
+
Error: toLogicalItems(Error)
|
|
1244
1276
|
};
|
|
1245
1277
|
}
|
|
1246
1278
|
async getStorageAcl(options) {
|
|
@@ -1311,16 +1343,18 @@ class StorageService {
|
|
|
1311
1343
|
* @returns {Promise<IListFileInfo[]>}
|
|
1312
1344
|
*/
|
|
1313
1345
|
async walkCloudDir(prefix, marker) {
|
|
1314
|
-
const { bucket, region } = this.getStorageConfig();
|
|
1346
|
+
const { bucket, region, basePath } = this.getStorageConfig();
|
|
1315
1347
|
return this.walkCloudDirCustom({
|
|
1316
1348
|
prefix,
|
|
1317
1349
|
bucket,
|
|
1318
1350
|
region,
|
|
1319
|
-
marker
|
|
1351
|
+
marker,
|
|
1352
|
+
basePath
|
|
1320
1353
|
});
|
|
1321
1354
|
}
|
|
1322
1355
|
/**
|
|
1323
1356
|
* 遍历云端文件夹,支持自定义 Bucket 和 Region
|
|
1357
|
+
* 不会自动拼接环境的共享桶 BasePath,需要时显式传入 basePath;传入后返回的 Key 为逻辑路径
|
|
1324
1358
|
* @param {string} prefix
|
|
1325
1359
|
* @param {string} [marker]
|
|
1326
1360
|
* @param {string} bucket
|
|
@@ -1328,12 +1362,13 @@ class StorageService {
|
|
|
1328
1362
|
* @returns {Promise<IListFileInfo[]>}
|
|
1329
1363
|
*/
|
|
1330
1364
|
async walkCloudDirCustom(options) {
|
|
1331
|
-
const { prefix, bucket, region, marker = '/' } = options;
|
|
1365
|
+
const { prefix, bucket, region, marker = '/', basePath = '' } = options;
|
|
1332
1366
|
let fileList = [];
|
|
1333
1367
|
const cos = this.getCos();
|
|
1334
1368
|
const getBucket = util_1.default.promisify(cos.getBucket).bind(cos);
|
|
1335
|
-
const prefixKey = this.getCloudKey(prefix);
|
|
1336
|
-
|
|
1369
|
+
const prefixKey = (0, storage_config_1.toPhysicalKey)(basePath, this.getCloudKey(prefix));
|
|
1370
|
+
// Marker 按字典序生效:默认 '/' 用于排除 . 开头的 Key,共享桶下同样放到 basePath 之后(如 base//)
|
|
1371
|
+
let currentMarker = marker ? (0, storage_config_1.toPhysicalKey)(basePath, '').concat(marker) : marker;
|
|
1337
1372
|
let isTruncated = true;
|
|
1338
1373
|
// 使用循环替代递归
|
|
1339
1374
|
while (isTruncated) {
|
|
@@ -1350,7 +1385,10 @@ class StorageService {
|
|
|
1350
1385
|
currentMarker = res.NextMarker;
|
|
1351
1386
|
}
|
|
1352
1387
|
}
|
|
1353
|
-
|
|
1388
|
+
if (!basePath) {
|
|
1389
|
+
return fileList;
|
|
1390
|
+
}
|
|
1391
|
+
return fileList.map(file => (Object.assign(Object.assign({}, file), { Key: (0, storage_config_1.toLogicalPath)(basePath, file.Key) })));
|
|
1354
1392
|
}
|
|
1355
1393
|
/**
|
|
1356
1394
|
* 列出指定目录下的文件和子目录(不递归,只列当前层)
|
|
@@ -1359,7 +1397,7 @@ class StorageService {
|
|
|
1359
1397
|
* @returns {Promise<{ files: IListFileInfo[], directories: string[] }>}
|
|
1360
1398
|
*/
|
|
1361
1399
|
async listCurrentDirectory(dirPath = '') {
|
|
1362
|
-
const { bucket, region } = this.getStorageConfig();
|
|
1400
|
+
const { bucket, region, basePath } = this.getStorageConfig();
|
|
1363
1401
|
const cos = this.getCos();
|
|
1364
1402
|
const getBucket = util_1.default.promisify(cos.getBucket).bind(cos);
|
|
1365
1403
|
// 确保目录路径以 / 结尾(除了根目录)
|
|
@@ -1367,6 +1405,7 @@ class StorageService {
|
|
|
1367
1405
|
if (prefix && !prefix.endsWith('/')) {
|
|
1368
1406
|
prefix = prefix + '/';
|
|
1369
1407
|
}
|
|
1408
|
+
const physicalPrefix = (0, storage_config_1.toPhysicalKey)(basePath, prefix);
|
|
1370
1409
|
const files = [];
|
|
1371
1410
|
const directories = [];
|
|
1372
1411
|
let marker = '';
|
|
@@ -1375,20 +1414,20 @@ class StorageService {
|
|
|
1375
1414
|
const res = await getBucket({
|
|
1376
1415
|
Bucket: bucket,
|
|
1377
1416
|
Region: region,
|
|
1378
|
-
Prefix:
|
|
1417
|
+
Prefix: physicalPrefix,
|
|
1379
1418
|
Delimiter: '/', // 关键:使用分隔符,不递归子目录
|
|
1380
1419
|
MaxKeys: 1000,
|
|
1381
1420
|
Marker: marker
|
|
1382
1421
|
});
|
|
1383
|
-
//
|
|
1422
|
+
// 文件列表(不包含子目录),Key 映射回逻辑路径
|
|
1384
1423
|
if (res.Contents) {
|
|
1385
1424
|
// 过滤掉目录本身(Key === prefix 的情况)
|
|
1386
|
-
const fileItems = res.Contents.filter((item) => item.Key !==
|
|
1425
|
+
const fileItems = res.Contents.filter((item) => item.Key !== physicalPrefix).map((item) => (Object.assign(Object.assign({}, item), { Key: (0, storage_config_1.toLogicalPath)(basePath, item.Key) })));
|
|
1387
1426
|
files.push(...fileItems);
|
|
1388
1427
|
}
|
|
1389
1428
|
// 子目录列表(CommonPrefixes)
|
|
1390
1429
|
if (res.CommonPrefixes) {
|
|
1391
|
-
const dirs = res.CommonPrefixes.map((item) => item.Prefix);
|
|
1430
|
+
const dirs = res.CommonPrefixes.map((item) => (0, storage_config_1.toLogicalPath)(basePath, item.Prefix));
|
|
1392
1431
|
directories.push(...dirs);
|
|
1393
1432
|
}
|
|
1394
1433
|
isTruncated = res.IsTruncated === 'true' || res.IsTruncated === true;
|
|
@@ -1520,18 +1559,21 @@ class StorageService {
|
|
|
1520
1559
|
*/
|
|
1521
1560
|
async getBucket(options) {
|
|
1522
1561
|
// const { bucket } = this.getStorageConfig()
|
|
1523
|
-
const { prefix, maxKeys, marker, bucket, region } = options;
|
|
1562
|
+
const { prefix, maxKeys, marker, bucket, region, basePath = '' } = options;
|
|
1524
1563
|
const cos = this.getCos();
|
|
1525
1564
|
const getBucket = util_1.default.promisify(cos.getBucket).bind(cos);
|
|
1526
|
-
const prefixKey = this.getCloudKey(prefix);
|
|
1565
|
+
const prefixKey = (0, storage_config_1.toPhysicalKey)(basePath, this.getCloudKey(prefix));
|
|
1527
1566
|
const res = await getBucket({
|
|
1528
1567
|
Bucket: bucket,
|
|
1529
1568
|
Region: region,
|
|
1530
1569
|
Prefix: prefixKey,
|
|
1531
1570
|
MaxKeys: maxKeys,
|
|
1532
|
-
Marker: marker
|
|
1571
|
+
Marker: basePath && marker ? (0, storage_config_1.toPhysicalKey)(basePath, marker) : marker
|
|
1533
1572
|
});
|
|
1534
|
-
|
|
1573
|
+
if (!basePath) {
|
|
1574
|
+
return res;
|
|
1575
|
+
}
|
|
1576
|
+
return Object.assign(Object.assign({}, res), { Prefix: res.Prefix ? (0, storage_config_1.toLogicalPath)(basePath, res.Prefix) : res.Prefix, Contents: (res.Contents || []).map(item => (Object.assign(Object.assign({}, item), { Key: (0, storage_config_1.toLogicalPath)(basePath, item.Key) }))), NextMarker: res.NextMarker ? (0, storage_config_1.toLogicalPath)(basePath, res.NextMarker) : res.NextMarker });
|
|
1535
1577
|
}
|
|
1536
1578
|
// ===== 复制/移动/搜索方法 =====
|
|
1537
1579
|
/**
|
|
@@ -1544,7 +1586,7 @@ class StorageService {
|
|
|
1544
1586
|
*/
|
|
1545
1587
|
async copyFile(options) {
|
|
1546
1588
|
const { sourcePath, destPath, force = false, skipExisting = false } = options;
|
|
1547
|
-
const { bucket, region } = this.getStorageConfig();
|
|
1589
|
+
const { bucket, region, basePath } = this.getStorageConfig();
|
|
1548
1590
|
// 1. 检查源文件是否存在
|
|
1549
1591
|
let sourceFileInfo;
|
|
1550
1592
|
try {
|
|
@@ -1593,11 +1635,11 @@ class StorageService {
|
|
|
1593
1635
|
// CopySource 格式: {bucket}.cos.{region}.myqcloud.com/{key}
|
|
1594
1636
|
// 注意: key 需要 URL 编码
|
|
1595
1637
|
// cspell:ignore myqcloud
|
|
1596
|
-
const copySource = `${bucket}.cos.${region}.myqcloud.com/${encodeURIComponent(sourcePath)}`;
|
|
1638
|
+
const copySource = `${bucket}.cos.${region}.myqcloud.com/${encodeURIComponent((0, storage_config_1.toPhysicalKey)(basePath, sourcePath))}`;
|
|
1597
1639
|
const res = await putObjectCopy({
|
|
1598
1640
|
Bucket: bucket,
|
|
1599
1641
|
Region: region,
|
|
1600
|
-
Key: destPath,
|
|
1642
|
+
Key: (0, storage_config_1.toPhysicalKey)(basePath, destPath),
|
|
1601
1643
|
CopySource: copySource
|
|
1602
1644
|
});
|
|
1603
1645
|
if (res.statusCode !== 200) {
|
|
@@ -1725,11 +1767,11 @@ class StorageService {
|
|
|
1725
1767
|
const result = await this.copyFile(options);
|
|
1726
1768
|
if (result.status === 'copied') {
|
|
1727
1769
|
// 复制成功后使用 COS SDK 直接删除源文件
|
|
1728
|
-
const { bucket, region } = this.getStorageConfig();
|
|
1770
|
+
const { bucket, region, basePath } = this.getStorageConfig();
|
|
1729
1771
|
const cos = this.getCos();
|
|
1730
1772
|
const deleteObject = util_1.default.promisify(cos.deleteObject).bind(cos);
|
|
1731
1773
|
// 注意:文件路径不需要用 getCloudKey(),getCloudKey() 会在末尾加 /,用于目录
|
|
1732
|
-
const sourceKey = options.sourcePath;
|
|
1774
|
+
const sourceKey = (0, storage_config_1.toPhysicalKey)(basePath, options.sourcePath);
|
|
1733
1775
|
try {
|
|
1734
1776
|
await deleteObject({
|
|
1735
1777
|
Bucket: bucket,
|
|
@@ -1758,7 +1800,7 @@ class StorageService {
|
|
|
1758
1800
|
.map(r => r.sourcePath);
|
|
1759
1801
|
if (copiedFiles.length > 0) {
|
|
1760
1802
|
// 使用 COS SDK deleteMultipleObject 批量删除源文件
|
|
1761
|
-
const { bucket, region } = this.getStorageConfig();
|
|
1803
|
+
const { bucket, region, basePath } = this.getStorageConfig();
|
|
1762
1804
|
const cos = this.getCos();
|
|
1763
1805
|
const deleteMultipleObject = util_1.default.promisify(cos.deleteMultipleObject).bind(cos);
|
|
1764
1806
|
// COS deleteMultipleObject 单次最多删除 1000 个对象,这里按 500 分片
|
|
@@ -1775,12 +1817,12 @@ class StorageService {
|
|
|
1775
1817
|
const res = await deleteMultipleObject({
|
|
1776
1818
|
Bucket: bucket,
|
|
1777
1819
|
Region: region,
|
|
1778
|
-
Objects: group.map(file => ({ Key: file }))
|
|
1820
|
+
Objects: group.map(file => ({ Key: (0, storage_config_1.toPhysicalKey)(basePath, file) }))
|
|
1779
1821
|
});
|
|
1780
1822
|
// 检查部分删除失败的情况
|
|
1781
1823
|
if (res.Error && res.Error.length > 0) {
|
|
1782
1824
|
for (const err of res.Error) {
|
|
1783
|
-
deleteFailedFiles.set(err.Key, err.Message || 'Unknown error');
|
|
1825
|
+
deleteFailedFiles.set((0, storage_config_1.toLogicalPath)(basePath, err.Key), err.Message || 'Unknown error');
|
|
1784
1826
|
}
|
|
1785
1827
|
}
|
|
1786
1828
|
}
|
|
@@ -1908,7 +1950,7 @@ class StorageService {
|
|
|
1908
1950
|
// env / proxy
|
|
1909
1951
|
const authConfig = this.environment.getAuthConfig();
|
|
1910
1952
|
const { proxy } = authConfig;
|
|
1911
|
-
const
|
|
1953
|
+
const env = this.environment.getEnvId();
|
|
1912
1954
|
const envId = envIdOverride || env;
|
|
1913
1955
|
// 路径编码:保留 "/" 层级,仅编码每段
|
|
1914
1956
|
const encodedBucketId = encodeURIComponent(bucketId);
|
|
@@ -2019,7 +2061,7 @@ class StorageService {
|
|
|
2019
2061
|
if (!accessToken || typeof accessToken !== 'string') {
|
|
2020
2062
|
throw new error_1.CloudBaseError('accessToken 必须是非空字符串');
|
|
2021
2063
|
}
|
|
2022
|
-
const
|
|
2064
|
+
const env = this.environment.getEnvId();
|
|
2023
2065
|
const envId = envIdOverride || env;
|
|
2024
2066
|
const encodedBucketId = encodeURIComponent(bucketId);
|
|
2025
2067
|
const encodedObjectName = objectName
|
|
@@ -2083,7 +2125,7 @@ class StorageService {
|
|
|
2083
2125
|
throw new error_1.CloudBaseError('prefixes 中的每一项必须是非空字符串');
|
|
2084
2126
|
}
|
|
2085
2127
|
}
|
|
2086
|
-
const
|
|
2128
|
+
const env = this.environment.getEnvId();
|
|
2087
2129
|
const envId = envIdOverride || env;
|
|
2088
2130
|
const encodedBucketId = encodeURIComponent(bucketId);
|
|
2089
2131
|
const url = `https://${envId}.api.tcloudbasegateway.com/v1/storages/object/${encodedBucketId}`;
|
|
@@ -2154,7 +2196,7 @@ class StorageService {
|
|
|
2154
2196
|
if (!accessToken || typeof accessToken !== 'string') {
|
|
2155
2197
|
throw new error_1.CloudBaseError('accessToken 必须是非空字符串');
|
|
2156
2198
|
}
|
|
2157
|
-
const
|
|
2199
|
+
const env = this.environment.getEnvId();
|
|
2158
2200
|
const envId = envIdOverride || env;
|
|
2159
2201
|
const url = `https://${envId}.api.tcloudbasegateway.com/v1/storages/object/copy`;
|
|
2160
2202
|
const headers = {
|
|
@@ -2233,7 +2275,7 @@ class StorageService {
|
|
|
2233
2275
|
if (!accessToken || typeof accessToken !== 'string') {
|
|
2234
2276
|
throw new error_1.CloudBaseError('accessToken 必须是非空字符串');
|
|
2235
2277
|
}
|
|
2236
|
-
const
|
|
2278
|
+
const env = this.environment.getEnvId();
|
|
2237
2279
|
const envId = envIdOverride || env;
|
|
2238
2280
|
const url = `https://${envId}.api.tcloudbasegateway.com/v1/storages/object/move`;
|
|
2239
2281
|
const body = {
|
|
@@ -2329,7 +2371,7 @@ class StorageService {
|
|
|
2329
2371
|
sortBy.order = normalizedOrder;
|
|
2330
2372
|
}
|
|
2331
2373
|
}
|
|
2332
|
-
const
|
|
2374
|
+
const env = this.environment.getEnvId();
|
|
2333
2375
|
const envId = envIdOverride || env;
|
|
2334
2376
|
const encodedBucketId = encodeURIComponent(bucketId);
|
|
2335
2377
|
const url = `https://${envId}.api.tcloudbasegateway.com/v1/storages/object/list/${encodedBucketId}`;
|
|
@@ -2401,7 +2443,7 @@ class StorageService {
|
|
|
2401
2443
|
if (!accessToken || typeof accessToken !== 'string') {
|
|
2402
2444
|
throw new error_1.CloudBaseError('accessToken 必须是非空字符串');
|
|
2403
2445
|
}
|
|
2404
|
-
const
|
|
2446
|
+
const env = this.environment.getEnvId();
|
|
2405
2447
|
const envId = envIdOverride || env;
|
|
2406
2448
|
const encodedBucketId = encodeURIComponent(bucketId);
|
|
2407
2449
|
const encodedObjectName = objectName
|
|
@@ -2475,7 +2517,7 @@ class StorageService {
|
|
|
2475
2517
|
if (!accessToken || typeof accessToken !== 'string') {
|
|
2476
2518
|
throw new error_1.CloudBaseError('accessToken 必须是非空字符串');
|
|
2477
2519
|
}
|
|
2478
|
-
const
|
|
2520
|
+
const env = this.environment.getEnvId();
|
|
2479
2521
|
const envId = envIdOverride || env;
|
|
2480
2522
|
const encodedBucketId = encodeURIComponent(bucketId);
|
|
2481
2523
|
const url = `https://${envId}.api.tcloudbasegateway.com/v1/storages/object/sign/${encodedBucketId}`;
|
|
@@ -2535,7 +2577,7 @@ class StorageService {
|
|
|
2535
2577
|
if (!accessToken || typeof accessToken !== 'string') {
|
|
2536
2578
|
throw new error_1.CloudBaseError('accessToken 必须是非空字符串');
|
|
2537
2579
|
}
|
|
2538
|
-
const
|
|
2580
|
+
const env = this.environment.getEnvId();
|
|
2539
2581
|
const envId = envIdOverride || env;
|
|
2540
2582
|
const encodedBucketId = encodeURIComponent(bucketId);
|
|
2541
2583
|
const encodedObjectName = objectName
|
|
@@ -2604,7 +2646,7 @@ class StorageService {
|
|
|
2604
2646
|
if (localPath && body) {
|
|
2605
2647
|
throw new error_1.CloudBaseError('localPath 与 body 只能二选一');
|
|
2606
2648
|
}
|
|
2607
|
-
const
|
|
2649
|
+
const env = this.environment.getEnvId();
|
|
2608
2650
|
const envId = envIdOverride || env;
|
|
2609
2651
|
const encodedBucketId = encodeURIComponent(bucketId);
|
|
2610
2652
|
const encodedObjectName = objectName
|
|
@@ -2694,7 +2736,7 @@ class StorageService {
|
|
|
2694
2736
|
if (!allowedMethods.includes(normalizedMethod)) {
|
|
2695
2737
|
throw new error_1.CloudBaseError('method 仅支持 GET 或 HEAD');
|
|
2696
2738
|
}
|
|
2697
|
-
const
|
|
2739
|
+
const env = this.environment.getEnvId();
|
|
2698
2740
|
const envId = envIdOverride || env;
|
|
2699
2741
|
const encodedBucketId = encodeURIComponent(bucketId);
|
|
2700
2742
|
const encodedObjectName = objectName
|
|
@@ -2782,7 +2824,7 @@ class StorageService {
|
|
|
2782
2824
|
if (!allowedMethods.includes(normalizedMethod)) {
|
|
2783
2825
|
throw new error_1.CloudBaseError('method 仅支持 GET 或 HEAD');
|
|
2784
2826
|
}
|
|
2785
|
-
const
|
|
2827
|
+
const env = this.environment.getEnvId();
|
|
2786
2828
|
const envId = envIdOverride || env;
|
|
2787
2829
|
const encodedBucketId = encodeURIComponent(bucketId);
|
|
2788
2830
|
const encodedObjectName = objectName
|
|
@@ -2868,7 +2910,7 @@ class StorageService {
|
|
|
2868
2910
|
if (!allowedMethods.includes(normalizedMethod)) {
|
|
2869
2911
|
throw new error_1.CloudBaseError('method 仅支持 GET 或 HEAD');
|
|
2870
2912
|
}
|
|
2871
|
-
const
|
|
2913
|
+
const env = this.environment.getEnvId();
|
|
2872
2914
|
const envId = envIdOverride || env;
|
|
2873
2915
|
const encodedBucketId = encodeURIComponent(bucketId);
|
|
2874
2916
|
const encodedObjectName = objectName
|
|
@@ -2934,7 +2976,7 @@ class StorageService {
|
|
|
2934
2976
|
if (!allowedMethods.includes(normalizedMethod)) {
|
|
2935
2977
|
throw new error_1.CloudBaseError('method 仅支持 GET 或 HEAD');
|
|
2936
2978
|
}
|
|
2937
|
-
const
|
|
2979
|
+
const env = this.environment.getEnvId();
|
|
2938
2980
|
const envId = envIdOverride || env;
|
|
2939
2981
|
const encodedBucketId = encodeURIComponent(bucketId);
|
|
2940
2982
|
const encodedObjectName = objectName
|
|
@@ -3000,7 +3042,7 @@ class StorageService {
|
|
|
3000
3042
|
if (!allowedMethods.includes(normalizedMethod)) {
|
|
3001
3043
|
throw new error_1.CloudBaseError('method 仅支持 GET 或 HEAD');
|
|
3002
3044
|
}
|
|
3003
|
-
const
|
|
3045
|
+
const env = this.environment.getEnvId();
|
|
3004
3046
|
const envId = envIdOverride || env;
|
|
3005
3047
|
const encodedBucketId = encodeURIComponent(bucketId);
|
|
3006
3048
|
const encodedObjectName = objectName
|
|
@@ -3075,7 +3117,7 @@ class StorageService {
|
|
|
3075
3117
|
if (!objectName || typeof objectName !== 'string') {
|
|
3076
3118
|
throw new error_1.CloudBaseError('objectName 必须是非空字符串');
|
|
3077
3119
|
}
|
|
3078
|
-
const
|
|
3120
|
+
const env = this.environment.getEnvId();
|
|
3079
3121
|
const envId = envIdOverride || env;
|
|
3080
3122
|
const encodedBucketId = encodeURIComponent(bucketId);
|
|
3081
3123
|
const encodedObjectName = objectName
|
|
@@ -3182,19 +3224,12 @@ class StorageService {
|
|
|
3182
3224
|
return `cloud://${env}.${bucket}/${cloudPath}`;
|
|
3183
3225
|
}
|
|
3184
3226
|
/**
|
|
3185
|
-
*
|
|
3227
|
+
* 获取存储桶配置,兼容共享桶(ExternalStorage)环境
|
|
3228
|
+
* basePath 为共享桶内的目录前缀,普通环境为空串
|
|
3186
3229
|
*/
|
|
3187
3230
|
getStorageConfig() {
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
const storageConfig = (_a = envConfig === null || envConfig === void 0 ? void 0 : envConfig.Storages) === null || _a === void 0 ? void 0 : _a[0];
|
|
3191
|
-
const { Region, Bucket } = storageConfig;
|
|
3192
|
-
const region = process.env.TCB_COS_REGION || Region;
|
|
3193
|
-
return {
|
|
3194
|
-
region,
|
|
3195
|
-
bucket: Bucket,
|
|
3196
|
-
env: envConfig.EnvId
|
|
3197
|
-
};
|
|
3231
|
+
const config = (0, storage_config_1.resolveStorageConfig)(this.environment.lazyEnvironmentConfig);
|
|
3232
|
+
return Object.assign(Object.assign({}, config), { region: process.env.TCB_COS_REGION || config.region });
|
|
3198
3233
|
}
|
|
3199
3234
|
/**
|
|
3200
3235
|
* 带重试功能的上传多文件函数
|
|
@@ -3399,6 +3434,7 @@ __decorate([
|
|
|
3399
3434
|
(0, utils_1.preLazy)()
|
|
3400
3435
|
/**
|
|
3401
3436
|
* 上传文件(支持精准重试失败文件)
|
|
3437
|
+
* 不会自动拼接环境的共享桶 BasePath,需要时显式传入 basePath;onFileFinish 回调中的 cloudFileKey 为逻辑路径
|
|
3402
3438
|
*
|
|
3403
3439
|
* 智能混合模式(默认):
|
|
3404
3440
|
* 1. 先并行快速上传(速度快)
|