@cloudbase/manager-node 5.3.0 → 5.4.0-beta.1

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.
@@ -1258,6 +1258,1237 @@ class StorageService {
1258
1258
  // 限制总数量
1259
1259
  return results.slice(0, limit);
1260
1260
  }
1261
+ /**
1262
+ * 通过 CloudBase HTTP API 上传对象(Binary 直传)
1263
+ * POST/PUT /v1/storages/object/:bucketId/*objectName
1264
+ */
1265
+ /* eslint-disable complexity */
1266
+ async uploadObject(options) {
1267
+ const { bucketId, objectName, localPath, body, contentType, contentLength, cacheControl, metadata, xRobotsTag, upsert = false, usePut = false, accessToken, envId: envIdOverride } = options;
1268
+ if (!bucketId || typeof bucketId !== 'string') {
1269
+ throw new error_1.CloudBaseError('bucketId 必须是非空字符串');
1270
+ }
1271
+ if (!objectName || typeof objectName !== 'string') {
1272
+ throw new error_1.CloudBaseError('objectName 必须是非空字符串');
1273
+ }
1274
+ if (!accessToken || typeof accessToken !== 'string') {
1275
+ throw new error_1.CloudBaseError('accessToken 必须是非空字符串');
1276
+ }
1277
+ if (!localPath && !body) {
1278
+ throw new error_1.CloudBaseError('localPath 与 body 至少提供一个');
1279
+ }
1280
+ if (localPath && body) {
1281
+ throw new error_1.CloudBaseError('localPath 与 body 只能二选一');
1282
+ }
1283
+ // env / proxy
1284
+ const authConfig = this.environment.getAuthConfig();
1285
+ const { proxy } = authConfig;
1286
+ const { env } = this.getStorageConfig();
1287
+ const envId = envIdOverride || env;
1288
+ // 路径编码:保留 "/" 层级,仅编码每段
1289
+ const encodedBucketId = encodeURIComponent(bucketId);
1290
+ const encodedObjectName = objectName
1291
+ .split('/')
1292
+ .map(seg => encodeURIComponent(seg))
1293
+ .join('/');
1294
+ const url = `https://${envId}.api.tcloudbasegateway.com/v1/storages/object/${encodedBucketId}/${encodedObjectName}`;
1295
+ const method = usePut ? 'PUT' : 'POST';
1296
+ // 推断文件大小
1297
+ let fileSize = contentLength;
1298
+ if (localPath) {
1299
+ const resolvedPath = path_1.default.resolve(localPath);
1300
+ (0, utils_1.checkFullAccess)(resolvedPath, true);
1301
+ const stat = fs_1.default.statSync(resolvedPath);
1302
+ if (!stat.isFile()) {
1303
+ throw new error_1.CloudBaseError(`localPath 不是文件: ${localPath}`);
1304
+ }
1305
+ fileSize = stat.size;
1306
+ }
1307
+ else if (Buffer.isBuffer(body) && fileSize === undefined) {
1308
+ fileSize = body.length;
1309
+ }
1310
+ // 文档约束:> 100MB 拒绝
1311
+ const LIMIT_100MB = 100 * 1024 * 1024;
1312
+ if (fileSize !== undefined && fileSize > LIMIT_100MB) {
1313
+ throw new error_1.CloudBaseError('FileSizeLimitExceeded: 文件大小超过 100MB', {
1314
+ code: 'FileSizeLimitExceeded'
1315
+ });
1316
+ }
1317
+ // 组装 Headers
1318
+ const headers = {
1319
+ Authorization: `Bearer ${accessToken}`,
1320
+ 'Content-Type': contentType || 'application/octet-stream'
1321
+ };
1322
+ if (fileSize !== undefined) {
1323
+ headers['Content-Length'] = String(fileSize);
1324
+ }
1325
+ if (cacheControl !== undefined && cacheControl !== null) {
1326
+ headers['Cache-Control'] =
1327
+ typeof cacheControl === 'number' ? `max-age=${cacheControl}` : String(cacheControl);
1328
+ }
1329
+ if (metadata) {
1330
+ // X-Metadata 放在 Header 里,具体大小限制由服务端/网关决定
1331
+ headers['X-Metadata'] = Buffer.from(JSON.stringify(metadata), 'utf8').toString('base64');
1332
+ }
1333
+ if (upsert) {
1334
+ headers['x-upsert'] = 'true';
1335
+ }
1336
+ if (xRobotsTag) {
1337
+ headers['X-Robots-Tag'] = xRobotsTag;
1338
+ }
1339
+ // 组装 Body:文件二进制本身
1340
+ const reqBody = localPath ? fs_1.default.createReadStream(path_1.default.resolve(localPath)) : body;
1341
+ const res = await (0, utils_1.fetchStream)(url, {
1342
+ method,
1343
+ headers,
1344
+ body: reqBody
1345
+ }, proxy);
1346
+ const text = await res.text();
1347
+ let payload = null;
1348
+ if (text) {
1349
+ try {
1350
+ payload = JSON.parse(text);
1351
+ }
1352
+ catch (_a) {
1353
+ if (!res.ok) {
1354
+ throw new error_1.CloudBaseError(`HTTP_${res.status}: ${text}`);
1355
+ }
1356
+ throw new error_1.CloudBaseError(`响应不是合法 JSON: ${text}`);
1357
+ }
1358
+ }
1359
+ if (!res.ok) {
1360
+ const code = (payload === null || payload === void 0 ? void 0 : payload.Code) || (payload === null || payload === void 0 ? void 0 : payload.code) || `HTTP_${res.status}`;
1361
+ const message = (payload === null || payload === void 0 ? void 0 : payload.Message) || (payload === null || payload === void 0 ? void 0 : payload.message) || res.statusText || '上传失败';
1362
+ throw new error_1.CloudBaseError(`${code}: ${message}`, {
1363
+ code,
1364
+ requestId: (payload === null || payload === void 0 ? void 0 : payload.RequestId) || (payload === null || payload === void 0 ? void 0 : payload.requestId)
1365
+ });
1366
+ }
1367
+ if (!payload || !payload.Id || !payload.Key) {
1368
+ throw new error_1.CloudBaseError('上传成功但响应格式异常:缺少 Id 或 Key');
1369
+ }
1370
+ return {
1371
+ Id: payload.Id,
1372
+ Key: payload.Key
1373
+ };
1374
+ }
1375
+ /**
1376
+ * 通过 CloudBase HTTP API 删除单个对象
1377
+ * DELETE /v1/storages/object/:bucketId/*objectName
1378
+ */
1379
+ async deleteObject(options) {
1380
+ const { bucketId, objectName, accessToken, envId: envIdOverride } = options;
1381
+ if (!bucketId || typeof bucketId !== 'string') {
1382
+ throw new error_1.CloudBaseError('bucketId 必须是非空字符串');
1383
+ }
1384
+ if (!objectName || typeof objectName !== 'string') {
1385
+ throw new error_1.CloudBaseError('objectName 必须是非空字符串');
1386
+ }
1387
+ if (!accessToken || typeof accessToken !== 'string') {
1388
+ throw new error_1.CloudBaseError('accessToken 必须是非空字符串');
1389
+ }
1390
+ const { env } = this.getStorageConfig();
1391
+ const envId = envIdOverride || env;
1392
+ const encodedBucketId = encodeURIComponent(bucketId);
1393
+ const encodedObjectName = objectName
1394
+ .split('/')
1395
+ .map(seg => encodeURIComponent(seg))
1396
+ .join('/');
1397
+ const url = `https://${envId}.api.tcloudbasegateway.com/v1/storages/object/${encodedBucketId}/${encodedObjectName}`;
1398
+ const { proxy } = this.environment.getAuthConfig();
1399
+ const res = await (0, utils_1.fetchStream)(url, {
1400
+ method: 'DELETE',
1401
+ headers: {
1402
+ Authorization: `Bearer ${accessToken}`
1403
+ }
1404
+ }, proxy);
1405
+ const text = await res.text();
1406
+ let payload = null;
1407
+ if (text) {
1408
+ try {
1409
+ payload = JSON.parse(text);
1410
+ }
1411
+ catch (_a) {
1412
+ if (!res.ok) {
1413
+ throw new error_1.CloudBaseError(`HTTP_${res.status}: ${text}`);
1414
+ }
1415
+ throw new error_1.CloudBaseError(`响应不是合法 JSON: ${text}`);
1416
+ }
1417
+ }
1418
+ if (!res.ok) {
1419
+ const code = (payload === null || payload === void 0 ? void 0 : payload.Code) || (payload === null || payload === void 0 ? void 0 : payload.code) || `HTTP_${res.status}`;
1420
+ const message = (payload === null || payload === void 0 ? void 0 : payload.Message) || (payload === null || payload === void 0 ? void 0 : payload.message) || res.statusText || '删除失败';
1421
+ throw new error_1.CloudBaseError(`${code}: ${message}`, {
1422
+ code,
1423
+ requestId: (payload === null || payload === void 0 ? void 0 : payload.RequestId) || (payload === null || payload === void 0 ? void 0 : payload.requestId)
1424
+ });
1425
+ }
1426
+ // 网关成功时返回 { message: 'Successfully deleted' }
1427
+ return {
1428
+ message: (payload === null || payload === void 0 ? void 0 : payload.message) || 'Successfully deleted'
1429
+ };
1430
+ }
1431
+ /**
1432
+ * 通过 CloudBase HTTP API 批量删除对象(单次最多 20 个)
1433
+ * DELETE /v1/storages/object/:bucketId
1434
+ */
1435
+ async deleteObjects(options) {
1436
+ const { bucketId, prefixes, accessToken, envId: envIdOverride } = options;
1437
+ if (!bucketId || typeof bucketId !== 'string') {
1438
+ throw new error_1.CloudBaseError('bucketId 必须是非空字符串');
1439
+ }
1440
+ if (!accessToken || typeof accessToken !== 'string') {
1441
+ throw new error_1.CloudBaseError('accessToken 必须是非空字符串');
1442
+ }
1443
+ if (!Array.isArray(prefixes) || prefixes.length === 0) {
1444
+ throw new error_1.CloudBaseError('prefixes 必须是非空数组');
1445
+ }
1446
+ if (prefixes.length > 20) {
1447
+ throw new error_1.CloudBaseError('prefixes 最多 20 个');
1448
+ }
1449
+ for (const name of prefixes) {
1450
+ if (!name || typeof name !== 'string') {
1451
+ throw new error_1.CloudBaseError('prefixes 中的每一项必须是非空字符串');
1452
+ }
1453
+ }
1454
+ const { env } = this.getStorageConfig();
1455
+ const envId = envIdOverride || env;
1456
+ const encodedBucketId = encodeURIComponent(bucketId);
1457
+ const url = `https://${envId}.api.tcloudbasegateway.com/v1/storages/object/${encodedBucketId}`;
1458
+ const { proxy } = this.environment.getAuthConfig();
1459
+ const res = await (0, utils_1.fetchStream)(url, {
1460
+ method: 'DELETE',
1461
+ headers: {
1462
+ Authorization: `Bearer ${accessToken}`,
1463
+ 'Content-Type': 'application/json'
1464
+ },
1465
+ body: JSON.stringify({ prefixes })
1466
+ }, proxy);
1467
+ const text = await res.text();
1468
+ let payload = null;
1469
+ if (text) {
1470
+ try {
1471
+ payload = JSON.parse(text);
1472
+ }
1473
+ catch (_a) {
1474
+ if (!res.ok) {
1475
+ throw new error_1.CloudBaseError(`HTTP_${res.status}: ${text}`);
1476
+ }
1477
+ throw new error_1.CloudBaseError(`响应不是合法 JSON: ${text}`);
1478
+ }
1479
+ }
1480
+ if (!res.ok) {
1481
+ const code = (payload === null || payload === void 0 ? void 0 : payload.Code) || (payload === null || payload === void 0 ? void 0 : payload.code) || `HTTP_${res.status}`;
1482
+ const message = (payload === null || payload === void 0 ? void 0 : payload.Message) || (payload === null || payload === void 0 ? void 0 : payload.message) || res.statusText || '批量删除失败';
1483
+ throw new error_1.CloudBaseError(`${code}: ${message}`, {
1484
+ code,
1485
+ requestId: (payload === null || payload === void 0 ? void 0 : payload.RequestId) || (payload === null || payload === void 0 ? void 0 : payload.requestId)
1486
+ });
1487
+ }
1488
+ if (!Array.isArray(payload)) {
1489
+ throw new error_1.CloudBaseError('批量删除响应格式异常:期望数组');
1490
+ }
1491
+ return payload;
1492
+ }
1493
+ /**
1494
+ * 通过 CloudBase HTTP API 复制对象
1495
+ * POST /v1/storages/object/copy
1496
+ */
1497
+ /* eslint-disable complexity */
1498
+ async copyObject(options) {
1499
+ const { bucketId, sourceKey, destinationKey, destinationBucket, metadata, userMetadata, copyMetadata, upsert, accessToken, envId: envIdOverride } = options;
1500
+ if (!bucketId || typeof bucketId !== 'string') {
1501
+ throw new error_1.CloudBaseError('bucketId 必须是非空字符串');
1502
+ }
1503
+ if (!sourceKey || typeof sourceKey !== 'string') {
1504
+ throw new error_1.CloudBaseError('sourceKey 必须是非空字符串');
1505
+ }
1506
+ if (!destinationKey || typeof destinationKey !== 'string') {
1507
+ throw new error_1.CloudBaseError('destinationKey 必须是非空字符串');
1508
+ }
1509
+ if (destinationBucket !== undefined &&
1510
+ (typeof destinationBucket !== 'string' || !destinationBucket)) {
1511
+ throw new error_1.CloudBaseError('destinationBucket 必须是非空字符串');
1512
+ }
1513
+ if (metadata !== undefined && (typeof metadata !== 'object' || metadata === null || Array.isArray(metadata))) {
1514
+ throw new error_1.CloudBaseError('metadata 必须是对象');
1515
+ }
1516
+ if (userMetadata !== undefined && (typeof userMetadata !== 'object' || userMetadata === null || Array.isArray(userMetadata))) {
1517
+ throw new error_1.CloudBaseError('userMetadata 必须是对象');
1518
+ }
1519
+ if (copyMetadata !== undefined && typeof copyMetadata !== 'boolean') {
1520
+ throw new error_1.CloudBaseError('copyMetadata 必须是布尔值');
1521
+ }
1522
+ if (!accessToken || typeof accessToken !== 'string') {
1523
+ throw new error_1.CloudBaseError('accessToken 必须是非空字符串');
1524
+ }
1525
+ const { env } = this.getStorageConfig();
1526
+ const envId = envIdOverride || env;
1527
+ const url = `https://${envId}.api.tcloudbasegateway.com/v1/storages/object/copy`;
1528
+ const headers = {
1529
+ Authorization: `Bearer ${accessToken}`,
1530
+ 'Content-Type': 'application/json'
1531
+ };
1532
+ if (upsert) {
1533
+ headers['x-upsert'] = 'true';
1534
+ }
1535
+ // copyMetadata=false 时,userMetadata 通过 x-metadata 头整体替换目标对象的 user_metadata
1536
+ if (copyMetadata === false && userMetadata) {
1537
+ headers['x-metadata'] = Buffer.from(JSON.stringify(userMetadata)).toString('base64');
1538
+ }
1539
+ const body = {
1540
+ bucketId,
1541
+ sourceKey,
1542
+ destinationKey
1543
+ };
1544
+ if (destinationBucket !== undefined)
1545
+ body.destinationBucket = destinationBucket;
1546
+ if (metadata !== undefined)
1547
+ body.metadata = metadata;
1548
+ if (copyMetadata !== undefined)
1549
+ body.copyMetadata = copyMetadata;
1550
+ const { proxy } = this.environment.getAuthConfig();
1551
+ const res = await (0, utils_1.fetchStream)(url, {
1552
+ method: 'POST',
1553
+ headers,
1554
+ body: JSON.stringify(body)
1555
+ }, proxy);
1556
+ const text = await res.text();
1557
+ let payload = null;
1558
+ if (text) {
1559
+ try {
1560
+ payload = JSON.parse(text);
1561
+ }
1562
+ catch (_a) {
1563
+ if (!res.ok) {
1564
+ throw new error_1.CloudBaseError(`HTTP_${res.status}: ${text}`);
1565
+ }
1566
+ throw new error_1.CloudBaseError(`响应不是合法 JSON: ${text}`);
1567
+ }
1568
+ }
1569
+ if (!res.ok) {
1570
+ const code = (payload === null || payload === void 0 ? void 0 : payload.Code) || (payload === null || payload === void 0 ? void 0 : payload.code) || `HTTP_${res.status}`;
1571
+ const message = (payload === null || payload === void 0 ? void 0 : payload.Message) || (payload === null || payload === void 0 ? void 0 : payload.message) || res.statusText || '复制失败';
1572
+ throw new error_1.CloudBaseError(`${code}: ${message}`, {
1573
+ code,
1574
+ requestId: (payload === null || payload === void 0 ? void 0 : payload.RequestId) || (payload === null || payload === void 0 ? void 0 : payload.requestId)
1575
+ });
1576
+ }
1577
+ if (!payload || !payload.Id || !payload.Key) {
1578
+ throw new error_1.CloudBaseError('复制成功但响应格式异常:缺少 Id 或 Key');
1579
+ }
1580
+ return payload;
1581
+ }
1582
+ /**
1583
+ * 通过 CloudBase HTTP API 移动对象(复制后删除源对象,由服务端一次性完成)
1584
+ * POST /v1/storages/object/move
1585
+ */
1586
+ async moveObject(options) {
1587
+ const { bucketId, sourceKey, destinationKey, destinationBucket, accessToken, envId: envIdOverride } = options;
1588
+ if (!bucketId || typeof bucketId !== 'string') {
1589
+ throw new error_1.CloudBaseError('bucketId 必须是非空字符串');
1590
+ }
1591
+ if (!sourceKey || typeof sourceKey !== 'string') {
1592
+ throw new error_1.CloudBaseError('sourceKey 必须是非空字符串');
1593
+ }
1594
+ if (!destinationKey || typeof destinationKey !== 'string') {
1595
+ throw new error_1.CloudBaseError('destinationKey 必须是非空字符串');
1596
+ }
1597
+ if (destinationBucket !== undefined &&
1598
+ (typeof destinationBucket !== 'string' || !destinationBucket)) {
1599
+ throw new error_1.CloudBaseError('destinationBucket 必须是非空字符串');
1600
+ }
1601
+ if (!accessToken || typeof accessToken !== 'string') {
1602
+ throw new error_1.CloudBaseError('accessToken 必须是非空字符串');
1603
+ }
1604
+ const { env } = this.getStorageConfig();
1605
+ const envId = envIdOverride || env;
1606
+ const url = `https://${envId}.api.tcloudbasegateway.com/v1/storages/object/move`;
1607
+ const body = {
1608
+ bucketId,
1609
+ sourceKey,
1610
+ destinationKey
1611
+ };
1612
+ if (destinationBucket !== undefined)
1613
+ body.destinationBucket = destinationBucket;
1614
+ const { proxy } = this.environment.getAuthConfig();
1615
+ const res = await (0, utils_1.fetchStream)(url, {
1616
+ method: 'POST',
1617
+ headers: {
1618
+ Authorization: `Bearer ${accessToken}`,
1619
+ 'Content-Type': 'application/json'
1620
+ },
1621
+ body: JSON.stringify(body)
1622
+ }, proxy);
1623
+ const text = await res.text();
1624
+ let payload = null;
1625
+ if (text) {
1626
+ try {
1627
+ payload = JSON.parse(text);
1628
+ }
1629
+ catch (_a) {
1630
+ if (!res.ok) {
1631
+ throw new error_1.CloudBaseError(`HTTP_${res.status}: ${text}`);
1632
+ }
1633
+ throw new error_1.CloudBaseError(`响应不是合法 JSON: ${text}`);
1634
+ }
1635
+ }
1636
+ if (!res.ok) {
1637
+ const code = (payload === null || payload === void 0 ? void 0 : payload.Code) || (payload === null || payload === void 0 ? void 0 : payload.code) || `HTTP_${res.status}`;
1638
+ const message = (payload === null || payload === void 0 ? void 0 : payload.Message) || (payload === null || payload === void 0 ? void 0 : payload.message) || res.statusText || '移动失败';
1639
+ throw new error_1.CloudBaseError(`${code}: ${message}`, {
1640
+ code,
1641
+ requestId: (payload === null || payload === void 0 ? void 0 : payload.RequestId) || (payload === null || payload === void 0 ? void 0 : payload.requestId)
1642
+ });
1643
+ }
1644
+ if (!payload || !payload.Id || !payload.Key) {
1645
+ throw new error_1.CloudBaseError('移动成功但响应格式异常:缺少 Id 或 Key');
1646
+ }
1647
+ return {
1648
+ message: payload.message || 'Successfully moved',
1649
+ Id: payload.Id,
1650
+ Key: payload.Key
1651
+ };
1652
+ }
1653
+ /**
1654
+ * 通过 CloudBase HTTP API 列出对象
1655
+ * POST /v1/storages/object/list/:bucketId
1656
+ */
1657
+ /* eslint-disable complexity */
1658
+ async listObjects(options) {
1659
+ const { bucketId, prefix, limit, cursor, withDelimiter, sortBy, accessToken, envId: envIdOverride } = options;
1660
+ if (!bucketId || typeof bucketId !== 'string') {
1661
+ throw new error_1.CloudBaseError('bucketId 必须是非空字符串');
1662
+ }
1663
+ if (prefix !== undefined && typeof prefix !== 'string') {
1664
+ throw new error_1.CloudBaseError('prefix 必须是字符串');
1665
+ }
1666
+ if (limit !== undefined) {
1667
+ if (!Number.isInteger(limit)) {
1668
+ throw new error_1.CloudBaseError('limit 必须是整数');
1669
+ }
1670
+ if (limit < 1 || limit > 1000) {
1671
+ throw new error_1.CloudBaseError('limit 必须在 1 ~ 1000 之间');
1672
+ }
1673
+ }
1674
+ if (cursor !== undefined && typeof cursor !== 'string') {
1675
+ throw new error_1.CloudBaseError('cursor 必须是字符串');
1676
+ }
1677
+ if (withDelimiter !== undefined && typeof withDelimiter !== 'boolean') {
1678
+ throw new error_1.CloudBaseError('withDelimiter 必须是布尔值');
1679
+ }
1680
+ if (!accessToken || typeof accessToken !== 'string') {
1681
+ throw new error_1.CloudBaseError('accessToken 必须是非空字符串');
1682
+ }
1683
+ if (sortBy !== undefined) {
1684
+ if (typeof sortBy !== 'object' || sortBy === null || Array.isArray(sortBy)) {
1685
+ throw new error_1.CloudBaseError('sortBy 必须是对象');
1686
+ }
1687
+ const allowedColumns = ['name', 'created_at', 'updated_at'];
1688
+ const allowedOrders = ['asc', 'desc'];
1689
+ if (sortBy.column !== undefined && !allowedColumns.includes(sortBy.column)) {
1690
+ throw new error_1.CloudBaseError(`sortBy.column 非法,允许值:${allowedColumns.join('、')}`);
1691
+ }
1692
+ if (sortBy.order !== undefined) {
1693
+ const normalizedOrder = String(sortBy.order).toLowerCase();
1694
+ if (!allowedOrders.includes(normalizedOrder)) {
1695
+ throw new error_1.CloudBaseError(`sortBy.order 非法,允许值:${allowedOrders.join('、')}`);
1696
+ }
1697
+ sortBy.order = normalizedOrder;
1698
+ }
1699
+ }
1700
+ const { env } = this.getStorageConfig();
1701
+ const envId = envIdOverride || env;
1702
+ const encodedBucketId = encodeURIComponent(bucketId);
1703
+ const url = `https://${envId}.api.tcloudbasegateway.com/v1/storages/object/list/${encodedBucketId}`;
1704
+ const body = {};
1705
+ if (prefix !== undefined)
1706
+ body.prefix = prefix;
1707
+ if (limit !== undefined)
1708
+ body.limit = limit;
1709
+ if (cursor !== undefined)
1710
+ body.cursor = cursor;
1711
+ if (withDelimiter !== undefined)
1712
+ body.with_delimiter = withDelimiter;
1713
+ if (sortBy !== undefined)
1714
+ body.sortBy = sortBy;
1715
+ const { proxy } = this.environment.getAuthConfig();
1716
+ const res = await (0, utils_1.fetchStream)(url, {
1717
+ method: 'POST',
1718
+ headers: {
1719
+ Authorization: `Bearer ${accessToken}`,
1720
+ 'Content-Type': 'application/json'
1721
+ },
1722
+ body: JSON.stringify(body)
1723
+ }, proxy);
1724
+ const text = await res.text();
1725
+ let payload = null;
1726
+ if (text) {
1727
+ try {
1728
+ payload = JSON.parse(text);
1729
+ }
1730
+ catch (_a) {
1731
+ if (!res.ok) {
1732
+ throw new error_1.CloudBaseError(`HTTP_${res.status}: ${text}`);
1733
+ }
1734
+ throw new error_1.CloudBaseError(`响应不是合法 JSON: ${text}`);
1735
+ }
1736
+ }
1737
+ if (!res.ok) {
1738
+ const code = (payload === null || payload === void 0 ? void 0 : payload.Code) || (payload === null || payload === void 0 ? void 0 : payload.code) || `HTTP_${res.status}`;
1739
+ const message = (payload === null || payload === void 0 ? void 0 : payload.Message) || (payload === null || payload === void 0 ? void 0 : payload.message) || res.statusText || '列出对象失败';
1740
+ throw new error_1.CloudBaseError(`${code}: ${message}`, {
1741
+ code,
1742
+ requestId: (payload === null || payload === void 0 ? void 0 : payload.RequestId) || (payload === null || payload === void 0 ? void 0 : payload.requestId)
1743
+ });
1744
+ }
1745
+ if (!payload || !Array.isArray(payload.objects) || !Array.isArray(payload.folders)) {
1746
+ throw new error_1.CloudBaseError('列出对象成功但响应格式异常:缺少 folders 或 objects 数组');
1747
+ }
1748
+ const result = Object.assign({ folders: payload.folders, objects: payload.objects, hasNext: Boolean(payload.hasNext), nextCursor: payload.nextCursor, nextCursorKey: payload.nextCursorKey }, payload);
1749
+ return result;
1750
+ }
1751
+ /**
1752
+ * 通过 CloudBase HTTP API 生成签名下载 URL
1753
+ * POST /v1/storages/object/sign/:bucketId/*objectName
1754
+ */
1755
+ async signObject(options) {
1756
+ const { bucketId, objectName, expiresIn, accessToken, envId: envIdOverride } = options;
1757
+ if (!bucketId || typeof bucketId !== 'string') {
1758
+ throw new error_1.CloudBaseError('bucketId 必须是非空字符串');
1759
+ }
1760
+ if (!objectName || typeof objectName !== 'string') {
1761
+ throw new error_1.CloudBaseError('objectName 必须是非空字符串');
1762
+ }
1763
+ if (!Number.isInteger(expiresIn)) {
1764
+ throw new error_1.CloudBaseError('expiresIn 必须是整数');
1765
+ }
1766
+ if (expiresIn < 1) {
1767
+ throw new error_1.CloudBaseError('expiresIn 必须 >= 1');
1768
+ }
1769
+ if (!accessToken || typeof accessToken !== 'string') {
1770
+ throw new error_1.CloudBaseError('accessToken 必须是非空字符串');
1771
+ }
1772
+ const { env } = this.getStorageConfig();
1773
+ const envId = envIdOverride || env;
1774
+ const encodedBucketId = encodeURIComponent(bucketId);
1775
+ const encodedObjectName = objectName
1776
+ .split('/')
1777
+ .map(seg => encodeURIComponent(seg))
1778
+ .join('/');
1779
+ const url = `https://${envId}.api.tcloudbasegateway.com/v1/storages/object/sign/${encodedBucketId}/${encodedObjectName}`;
1780
+ const { proxy } = this.environment.getAuthConfig();
1781
+ const res = await (0, utils_1.fetchStream)(url, {
1782
+ method: 'POST',
1783
+ headers: {
1784
+ Authorization: `Bearer ${accessToken}`,
1785
+ 'Content-Type': 'application/json'
1786
+ },
1787
+ body: JSON.stringify({ expiresIn })
1788
+ }, proxy);
1789
+ const text = await res.text();
1790
+ let payload = null;
1791
+ if (text) {
1792
+ try {
1793
+ payload = JSON.parse(text);
1794
+ }
1795
+ catch (_a) {
1796
+ if (!res.ok) {
1797
+ throw new error_1.CloudBaseError(`HTTP_${res.status}: ${text}`);
1798
+ }
1799
+ throw new error_1.CloudBaseError(`响应不是合法 JSON: ${text}`);
1800
+ }
1801
+ }
1802
+ if (!res.ok) {
1803
+ const code = (payload === null || payload === void 0 ? void 0 : payload.Code) || (payload === null || payload === void 0 ? void 0 : payload.code) || `HTTP_${res.status}`;
1804
+ const message = (payload === null || payload === void 0 ? void 0 : payload.Message) || (payload === null || payload === void 0 ? void 0 : payload.message) || res.statusText || '生成签名 URL 失败';
1805
+ throw new error_1.CloudBaseError(`${code}: ${message}`, {
1806
+ code,
1807
+ requestId: (payload === null || payload === void 0 ? void 0 : payload.RequestId) || (payload === null || payload === void 0 ? void 0 : payload.requestId)
1808
+ });
1809
+ }
1810
+ if (!payload || typeof payload.signedURL !== 'string' || !payload.signedURL) {
1811
+ throw new error_1.CloudBaseError('生成签名 URL 成功但响应格式异常:缺少 signedURL');
1812
+ }
1813
+ return {
1814
+ signedURL: payload.signedURL
1815
+ };
1816
+ }
1817
+ /**
1818
+ * 通过 CloudBase HTTP API 批量生成签名下载 URL
1819
+ * POST /v1/storages/object/sign/:bucketId
1820
+ */
1821
+ async signObjects(options) {
1822
+ const { bucketId, expiresIn, paths, accessToken, envId: envIdOverride } = options;
1823
+ if (!bucketId || typeof bucketId !== 'string') {
1824
+ throw new error_1.CloudBaseError('bucketId 必须是非空字符串');
1825
+ }
1826
+ if (!Number.isInteger(expiresIn)) {
1827
+ throw new error_1.CloudBaseError('expiresIn 必须是整数');
1828
+ }
1829
+ if (expiresIn < 1) {
1830
+ throw new error_1.CloudBaseError('expiresIn 必须 >= 1');
1831
+ }
1832
+ if (!Array.isArray(paths) || paths.length === 0) {
1833
+ throw new error_1.CloudBaseError('paths 必须是非空数组');
1834
+ }
1835
+ if (paths.length > 500) {
1836
+ throw new error_1.CloudBaseError('paths 最多 500 个');
1837
+ }
1838
+ for (const pathItem of paths) {
1839
+ if (!pathItem || typeof pathItem !== 'string') {
1840
+ throw new error_1.CloudBaseError('paths 中每一项必须是非空字符串');
1841
+ }
1842
+ }
1843
+ if (!accessToken || typeof accessToken !== 'string') {
1844
+ throw new error_1.CloudBaseError('accessToken 必须是非空字符串');
1845
+ }
1846
+ const { env } = this.getStorageConfig();
1847
+ const envId = envIdOverride || env;
1848
+ const encodedBucketId = encodeURIComponent(bucketId);
1849
+ const url = `https://${envId}.api.tcloudbasegateway.com/v1/storages/object/sign/${encodedBucketId}`;
1850
+ const { proxy } = this.environment.getAuthConfig();
1851
+ const res = await (0, utils_1.fetchStream)(url, {
1852
+ method: 'POST',
1853
+ headers: {
1854
+ Authorization: `Bearer ${accessToken}`,
1855
+ 'Content-Type': 'application/json'
1856
+ },
1857
+ body: JSON.stringify({
1858
+ expiresIn,
1859
+ paths
1860
+ })
1861
+ }, proxy);
1862
+ const text = await res.text();
1863
+ let payload = null;
1864
+ if (text) {
1865
+ try {
1866
+ payload = JSON.parse(text);
1867
+ }
1868
+ catch (_a) {
1869
+ if (!res.ok) {
1870
+ throw new error_1.CloudBaseError(`HTTP_${res.status}: ${text}`);
1871
+ }
1872
+ throw new error_1.CloudBaseError(`响应不是合法 JSON: ${text}`);
1873
+ }
1874
+ }
1875
+ if (!res.ok) {
1876
+ const code = (payload === null || payload === void 0 ? void 0 : payload.Code) || (payload === null || payload === void 0 ? void 0 : payload.code) || `HTTP_${res.status}`;
1877
+ const message = (payload === null || payload === void 0 ? void 0 : payload.Message) || (payload === null || payload === void 0 ? void 0 : payload.message) || res.statusText || '批量生成签名 URL 失败';
1878
+ throw new error_1.CloudBaseError(`${code}: ${message}`, {
1879
+ code,
1880
+ requestId: (payload === null || payload === void 0 ? void 0 : payload.RequestId) || (payload === null || payload === void 0 ? void 0 : payload.requestId)
1881
+ });
1882
+ }
1883
+ if (!Array.isArray(payload)) {
1884
+ throw new error_1.CloudBaseError('批量生成签名 URL 成功但响应格式异常:期望数组');
1885
+ }
1886
+ return payload;
1887
+ }
1888
+ /**
1889
+ * 通过 CloudBase HTTP API 生成签名上传 URL
1890
+ * POST /v1/storages/object/upload/sign/:bucketId/*objectName
1891
+ */
1892
+ async signUploadObject(options) {
1893
+ const { bucketId, objectName, upsert = false, accessToken, envId: envIdOverride } = options;
1894
+ if (!bucketId || typeof bucketId !== 'string') {
1895
+ throw new error_1.CloudBaseError('bucketId 必须是非空字符串');
1896
+ }
1897
+ if (!objectName || typeof objectName !== 'string') {
1898
+ throw new error_1.CloudBaseError('objectName 必须是非空字符串');
1899
+ }
1900
+ if (typeof upsert !== 'boolean') {
1901
+ throw new error_1.CloudBaseError('upsert 必须是布尔值');
1902
+ }
1903
+ if (!accessToken || typeof accessToken !== 'string') {
1904
+ throw new error_1.CloudBaseError('accessToken 必须是非空字符串');
1905
+ }
1906
+ const { env } = this.getStorageConfig();
1907
+ const envId = envIdOverride || env;
1908
+ const encodedBucketId = encodeURIComponent(bucketId);
1909
+ const encodedObjectName = objectName
1910
+ .split('/')
1911
+ .map(seg => encodeURIComponent(seg))
1912
+ .join('/');
1913
+ const url = `https://${envId}.api.tcloudbasegateway.com/v1/storages/object/upload/sign/${encodedBucketId}/${encodedObjectName}`;
1914
+ const headers = {
1915
+ Authorization: `Bearer ${accessToken}`
1916
+ };
1917
+ if (upsert) {
1918
+ headers['x-upsert'] = 'true';
1919
+ }
1920
+ const { proxy } = this.environment.getAuthConfig();
1921
+ const res = await (0, utils_1.fetchStream)(url, {
1922
+ method: 'POST',
1923
+ headers
1924
+ }, proxy);
1925
+ const text = await res.text();
1926
+ let payload = null;
1927
+ if (text) {
1928
+ try {
1929
+ payload = JSON.parse(text);
1930
+ }
1931
+ catch (_a) {
1932
+ if (!res.ok) {
1933
+ throw new error_1.CloudBaseError(`HTTP_${res.status}: ${text}`);
1934
+ }
1935
+ throw new error_1.CloudBaseError(`响应不是合法 JSON: ${text}`);
1936
+ }
1937
+ }
1938
+ if (!res.ok) {
1939
+ const code = (payload === null || payload === void 0 ? void 0 : payload.Code) || (payload === null || payload === void 0 ? void 0 : payload.code) || `HTTP_${res.status}`;
1940
+ const message = (payload === null || payload === void 0 ? void 0 : payload.Message) || (payload === null || payload === void 0 ? void 0 : payload.message) || res.statusText || '生成签名上传 URL 失败';
1941
+ throw new error_1.CloudBaseError(`${code}: ${message}`, {
1942
+ code,
1943
+ requestId: (payload === null || payload === void 0 ? void 0 : payload.RequestId) || (payload === null || payload === void 0 ? void 0 : payload.requestId)
1944
+ });
1945
+ }
1946
+ if (!payload || typeof payload.url !== 'string' || typeof payload.token !== 'string') {
1947
+ throw new error_1.CloudBaseError('生成签名上传 URL 成功但响应格式异常:缺少 url 或 token');
1948
+ }
1949
+ return {
1950
+ url: payload.url,
1951
+ token: payload.token
1952
+ };
1953
+ }
1954
+ /**
1955
+ * 通过 CloudBase HTTP API 使用签名 URL 上传对象(无需 Authorization)
1956
+ * PUT /v1/storages/object/upload/sign/:bucketId/*objectName?token=xxx
1957
+ */
1958
+ async uploadObjectBySign(options) {
1959
+ const { bucketId, objectName, token, localPath, body, contentType, contentLength, envId: envIdOverride } = options;
1960
+ if (!bucketId || typeof bucketId !== 'string') {
1961
+ throw new error_1.CloudBaseError('bucketId 必须是非空字符串');
1962
+ }
1963
+ if (!objectName || typeof objectName !== 'string') {
1964
+ throw new error_1.CloudBaseError('objectName 必须是非空字符串');
1965
+ }
1966
+ if (!token || typeof token !== 'string') {
1967
+ throw new error_1.CloudBaseError('token 必须是非空字符串');
1968
+ }
1969
+ if (!localPath && !body) {
1970
+ throw new error_1.CloudBaseError('localPath 与 body 至少提供一个');
1971
+ }
1972
+ if (localPath && body) {
1973
+ throw new error_1.CloudBaseError('localPath 与 body 只能二选一');
1974
+ }
1975
+ const { env } = this.getStorageConfig();
1976
+ const envId = envIdOverride || env;
1977
+ const encodedBucketId = encodeURIComponent(bucketId);
1978
+ const encodedObjectName = objectName
1979
+ .split('/')
1980
+ .map(seg => encodeURIComponent(seg))
1981
+ .join('/');
1982
+ const encodedToken = encodeURIComponent(token);
1983
+ const url = `https://${envId}.api.tcloudbasegateway.com/v1/storages/object/upload/sign/${encodedBucketId}/${encodedObjectName}?token=${encodedToken}`;
1984
+ let fileSize = contentLength;
1985
+ if (localPath) {
1986
+ const resolvedPath = path_1.default.resolve(localPath);
1987
+ (0, utils_1.checkFullAccess)(resolvedPath, true);
1988
+ const stat = fs_1.default.statSync(resolvedPath);
1989
+ if (!stat.isFile()) {
1990
+ throw new error_1.CloudBaseError(`localPath 不是文件: ${localPath}`);
1991
+ }
1992
+ fileSize = stat.size;
1993
+ }
1994
+ else if (Buffer.isBuffer(body) && fileSize === undefined) {
1995
+ fileSize = body.length;
1996
+ }
1997
+ const headers = {
1998
+ 'Content-Type': contentType || 'application/octet-stream'
1999
+ };
2000
+ if (fileSize !== undefined) {
2001
+ headers['Content-Length'] = String(fileSize);
2002
+ }
2003
+ const reqBody = localPath ? fs_1.default.createReadStream(path_1.default.resolve(localPath)) : body;
2004
+ const { proxy } = this.environment.getAuthConfig();
2005
+ const res = await (0, utils_1.fetchStream)(url, {
2006
+ method: 'PUT',
2007
+ headers,
2008
+ body: reqBody
2009
+ }, proxy);
2010
+ const text = await res.text();
2011
+ let payload = null;
2012
+ if (text) {
2013
+ try {
2014
+ payload = JSON.parse(text);
2015
+ }
2016
+ catch (_a) {
2017
+ if (!res.ok) {
2018
+ throw new error_1.CloudBaseError(`HTTP_${res.status}: ${text}`);
2019
+ }
2020
+ throw new error_1.CloudBaseError(`响应不是合法 JSON: ${text}`);
2021
+ }
2022
+ }
2023
+ if (!res.ok) {
2024
+ const code = (payload === null || payload === void 0 ? void 0 : payload.Code) || (payload === null || payload === void 0 ? void 0 : payload.code) || `HTTP_${res.status}`;
2025
+ const message = (payload === null || payload === void 0 ? void 0 : payload.Message) || (payload === null || payload === void 0 ? void 0 : payload.message) || res.statusText || '签名上传失败';
2026
+ throw new error_1.CloudBaseError(`${code}: ${message}`, {
2027
+ code,
2028
+ requestId: (payload === null || payload === void 0 ? void 0 : payload.RequestId) || (payload === null || payload === void 0 ? void 0 : payload.requestId)
2029
+ });
2030
+ }
2031
+ if (!payload || typeof payload.Key !== 'string' || !payload.Key) {
2032
+ throw new error_1.CloudBaseError('签名上传成功但响应格式异常:缺少 Key');
2033
+ }
2034
+ return {
2035
+ Key: payload.Key
2036
+ };
2037
+ }
2038
+ /**
2039
+ * 通过 CloudBase HTTP API 下载对象(需认证)
2040
+ * GET/HEAD /v1/storages/object/authenticated/:bucketId/*objectName
2041
+ */
2042
+ async downloadAuthenticatedObject(options) {
2043
+ const { bucketId, objectName, method = 'GET', download, ifNoneMatch, ifModifiedSince, range, accessToken, envId: envIdOverride } = options;
2044
+ if (!bucketId || typeof bucketId !== 'string') {
2045
+ throw new error_1.CloudBaseError('bucketId 必须是非空字符串');
2046
+ }
2047
+ if (!objectName || typeof objectName !== 'string') {
2048
+ throw new error_1.CloudBaseError('objectName 必须是非空字符串');
2049
+ }
2050
+ if (!accessToken || typeof accessToken !== 'string') {
2051
+ throw new error_1.CloudBaseError('accessToken 必须是非空字符串');
2052
+ }
2053
+ const normalizedMethod = String(method).toUpperCase();
2054
+ const allowedMethods = ['GET', 'HEAD'];
2055
+ if (!allowedMethods.includes(normalizedMethod)) {
2056
+ throw new error_1.CloudBaseError('method 仅支持 GET 或 HEAD');
2057
+ }
2058
+ const { env } = this.getStorageConfig();
2059
+ const envId = envIdOverride || env;
2060
+ const encodedBucketId = encodeURIComponent(bucketId);
2061
+ const encodedObjectName = objectName
2062
+ .split('/')
2063
+ .map(seg => encodeURIComponent(seg))
2064
+ .join('/');
2065
+ let url = `https://${envId}.api.tcloudbasegateway.com/v1/storages/object/authenticated/${encodedBucketId}/${encodedObjectName}`;
2066
+ if (download !== undefined) {
2067
+ if (typeof download !== 'string') {
2068
+ throw new error_1.CloudBaseError('download 必须是字符串');
2069
+ }
2070
+ url += `?download=${encodeURIComponent(download)}`;
2071
+ }
2072
+ const headers = {
2073
+ Authorization: `Bearer ${accessToken}`
2074
+ };
2075
+ if (ifNoneMatch !== undefined) {
2076
+ if (typeof ifNoneMatch !== 'string') {
2077
+ throw new error_1.CloudBaseError('ifNoneMatch 必须是字符串');
2078
+ }
2079
+ headers['If-None-Match'] = ifNoneMatch;
2080
+ }
2081
+ if (ifModifiedSince !== undefined) {
2082
+ if (typeof ifModifiedSince !== 'string') {
2083
+ throw new error_1.CloudBaseError('ifModifiedSince 必须是字符串');
2084
+ }
2085
+ headers['If-Modified-Since'] = ifModifiedSince;
2086
+ }
2087
+ if (range !== undefined) {
2088
+ if (typeof range !== 'string') {
2089
+ throw new error_1.CloudBaseError('range 必须是字符串');
2090
+ }
2091
+ headers.Range = range;
2092
+ }
2093
+ const { proxy } = this.environment.getAuthConfig();
2094
+ const res = await (0, utils_1.fetchStream)(url, {
2095
+ method: normalizedMethod,
2096
+ headers
2097
+ }, proxy);
2098
+ if (!res.ok) {
2099
+ const text = await res.text();
2100
+ let payload = null;
2101
+ if (text) {
2102
+ try {
2103
+ payload = JSON.parse(text);
2104
+ }
2105
+ catch (_a) {
2106
+ throw new error_1.CloudBaseError(`HTTP_${res.status}: ${text}`);
2107
+ }
2108
+ }
2109
+ const code = (payload === null || payload === void 0 ? void 0 : payload.Code) || (payload === null || payload === void 0 ? void 0 : payload.code) || `HTTP_${res.status}`;
2110
+ const message = (payload === null || payload === void 0 ? void 0 : payload.Message) || (payload === null || payload === void 0 ? void 0 : payload.message) || res.statusText || '下载对象失败';
2111
+ throw new error_1.CloudBaseError(`${code}: ${message}`, {
2112
+ code,
2113
+ requestId: (payload === null || payload === void 0 ? void 0 : payload.RequestId) || (payload === null || payload === void 0 ? void 0 : payload.requestId)
2114
+ });
2115
+ }
2116
+ const responseHeaders = {};
2117
+ res.headers.forEach((value, key) => {
2118
+ responseHeaders[key] = value;
2119
+ });
2120
+ return {
2121
+ status: res.status,
2122
+ headers: responseHeaders,
2123
+ body: normalizedMethod === 'HEAD' ? null : res.body
2124
+ };
2125
+ }
2126
+ /**
2127
+ * 通过 CloudBase HTTP API 下载对象(可选认证)
2128
+ * GET/HEAD /v1/storages/object/:bucketId/*objectName
2129
+ */
2130
+ async downloadObject(options) {
2131
+ const { bucketId, objectName, method = 'GET', download, ifNoneMatch, ifModifiedSince, range, accessToken, envId: envIdOverride } = options;
2132
+ if (!bucketId || typeof bucketId !== 'string') {
2133
+ throw new error_1.CloudBaseError('bucketId 必须是非空字符串');
2134
+ }
2135
+ if (!objectName || typeof objectName !== 'string') {
2136
+ throw new error_1.CloudBaseError('objectName 必须是非空字符串');
2137
+ }
2138
+ if (accessToken !== undefined && typeof accessToken !== 'string') {
2139
+ throw new error_1.CloudBaseError('accessToken 必须是字符串');
2140
+ }
2141
+ const normalizedMethod = String(method).toUpperCase();
2142
+ const allowedMethods = ['GET', 'HEAD'];
2143
+ if (!allowedMethods.includes(normalizedMethod)) {
2144
+ throw new error_1.CloudBaseError('method 仅支持 GET 或 HEAD');
2145
+ }
2146
+ const { env } = this.getStorageConfig();
2147
+ const envId = envIdOverride || env;
2148
+ const encodedBucketId = encodeURIComponent(bucketId);
2149
+ const encodedObjectName = objectName
2150
+ .split('/')
2151
+ .map(seg => encodeURIComponent(seg))
2152
+ .join('/');
2153
+ let url = `https://${envId}.api.tcloudbasegateway.com/v1/storages/object/${encodedBucketId}/${encodedObjectName}`;
2154
+ if (download !== undefined) {
2155
+ if (typeof download !== 'string') {
2156
+ throw new error_1.CloudBaseError('download 必须是字符串');
2157
+ }
2158
+ url += `?download=${encodeURIComponent(download)}`;
2159
+ }
2160
+ const headers = {};
2161
+ if (accessToken) {
2162
+ headers.Authorization = `Bearer ${accessToken}`;
2163
+ }
2164
+ if (ifNoneMatch !== undefined) {
2165
+ if (typeof ifNoneMatch !== 'string') {
2166
+ throw new error_1.CloudBaseError('ifNoneMatch 必须是字符串');
2167
+ }
2168
+ headers['If-None-Match'] = ifNoneMatch;
2169
+ }
2170
+ if (ifModifiedSince !== undefined) {
2171
+ if (typeof ifModifiedSince !== 'string') {
2172
+ throw new error_1.CloudBaseError('ifModifiedSince 必须是字符串');
2173
+ }
2174
+ headers['If-Modified-Since'] = ifModifiedSince;
2175
+ }
2176
+ if (range !== undefined) {
2177
+ if (typeof range !== 'string') {
2178
+ throw new error_1.CloudBaseError('range 必须是字符串');
2179
+ }
2180
+ headers.Range = range;
2181
+ }
2182
+ const { proxy } = this.environment.getAuthConfig();
2183
+ const res = await (0, utils_1.fetchStream)(url, {
2184
+ method: normalizedMethod,
2185
+ headers
2186
+ }, proxy);
2187
+ if (!res.ok) {
2188
+ const text = await res.text();
2189
+ let payload = null;
2190
+ if (text) {
2191
+ try {
2192
+ payload = JSON.parse(text);
2193
+ }
2194
+ catch (_a) {
2195
+ throw new error_1.CloudBaseError(`HTTP_${res.status}: ${text}`);
2196
+ }
2197
+ }
2198
+ const code = (payload === null || payload === void 0 ? void 0 : payload.Code) || (payload === null || payload === void 0 ? void 0 : payload.code) || `HTTP_${res.status}`;
2199
+ const message = (payload === null || payload === void 0 ? void 0 : payload.Message) || (payload === null || payload === void 0 ? void 0 : payload.message) || res.statusText || '下载对象失败';
2200
+ throw new error_1.CloudBaseError(`${code}: ${message}`, {
2201
+ code,
2202
+ requestId: (payload === null || payload === void 0 ? void 0 : payload.RequestId) || (payload === null || payload === void 0 ? void 0 : payload.requestId)
2203
+ });
2204
+ }
2205
+ const responseHeaders = {};
2206
+ res.headers.forEach((value, key) => {
2207
+ responseHeaders[key] = value;
2208
+ });
2209
+ return {
2210
+ status: res.status,
2211
+ headers: responseHeaders,
2212
+ body: normalizedMethod === 'HEAD' ? null : res.body
2213
+ };
2214
+ }
2215
+ /**
2216
+ * 通过 CloudBase HTTP API 下载公开对象(无需认证)
2217
+ * GET/HEAD /v1/storages/object/public/:bucketId/*objectName
2218
+ */
2219
+ async downloadPublicObject(options) {
2220
+ const { bucketId, objectName, method = 'GET', download, envId: envIdOverride } = options;
2221
+ if (!bucketId || typeof bucketId !== 'string') {
2222
+ throw new error_1.CloudBaseError('bucketId 必须是非空字符串');
2223
+ }
2224
+ if (!objectName || typeof objectName !== 'string') {
2225
+ throw new error_1.CloudBaseError('objectName 必须是非空字符串');
2226
+ }
2227
+ const normalizedMethod = String(method).toUpperCase();
2228
+ const allowedMethods = ['GET', 'HEAD'];
2229
+ if (!allowedMethods.includes(normalizedMethod)) {
2230
+ throw new error_1.CloudBaseError('method 仅支持 GET 或 HEAD');
2231
+ }
2232
+ const { env } = this.getStorageConfig();
2233
+ const envId = envIdOverride || env;
2234
+ const encodedBucketId = encodeURIComponent(bucketId);
2235
+ const encodedObjectName = objectName
2236
+ .split('/')
2237
+ .map(seg => encodeURIComponent(seg))
2238
+ .join('/');
2239
+ let url = `https://${envId}.api.tcloudbasegateway.com/v1/storages/object/public/${encodedBucketId}/${encodedObjectName}`;
2240
+ if (download !== undefined) {
2241
+ if (typeof download !== 'string') {
2242
+ throw new error_1.CloudBaseError('download 必须是字符串');
2243
+ }
2244
+ url += `?download=${encodeURIComponent(download)}`;
2245
+ }
2246
+ const { proxy } = this.environment.getAuthConfig();
2247
+ const res = await (0, utils_1.fetchStream)(url, {
2248
+ method: normalizedMethod
2249
+ }, proxy);
2250
+ if (!res.ok) {
2251
+ const text = await res.text();
2252
+ let payload = null;
2253
+ if (text) {
2254
+ try {
2255
+ payload = JSON.parse(text);
2256
+ }
2257
+ catch (_a) {
2258
+ throw new error_1.CloudBaseError(`HTTP_${res.status}: ${text}`);
2259
+ }
2260
+ }
2261
+ const code = (payload === null || payload === void 0 ? void 0 : payload.Code) || (payload === null || payload === void 0 ? void 0 : payload.code) || `HTTP_${res.status}`;
2262
+ const message = (payload === null || payload === void 0 ? void 0 : payload.Message) || (payload === null || payload === void 0 ? void 0 : payload.message) || res.statusText || '下载公开对象失败';
2263
+ throw new error_1.CloudBaseError(`${code}: ${message}`, {
2264
+ code,
2265
+ requestId: (payload === null || payload === void 0 ? void 0 : payload.RequestId) || (payload === null || payload === void 0 ? void 0 : payload.requestId)
2266
+ });
2267
+ }
2268
+ const responseHeaders = {};
2269
+ res.headers.forEach((value, key) => {
2270
+ responseHeaders[key] = value;
2271
+ });
2272
+ return {
2273
+ status: res.status,
2274
+ headers: responseHeaders,
2275
+ body: normalizedMethod === 'HEAD' ? null : res.body
2276
+ };
2277
+ }
2278
+ /**
2279
+ * 通过 CloudBase HTTP API 使用签名 URL 下载对象(无需 Authorization)
2280
+ * GET/HEAD /v1/storages/object/sign/:bucketId/*objectName?token=xxx
2281
+ */
2282
+ async downloadObjectBySign(options) {
2283
+ const { bucketId, objectName, token, method = 'GET', download, envId: envIdOverride } = options;
2284
+ if (!bucketId || typeof bucketId !== 'string') {
2285
+ throw new error_1.CloudBaseError('bucketId 必须是非空字符串');
2286
+ }
2287
+ if (!objectName || typeof objectName !== 'string') {
2288
+ throw new error_1.CloudBaseError('objectName 必须是非空字符串');
2289
+ }
2290
+ if (!token || typeof token !== 'string') {
2291
+ throw new error_1.CloudBaseError('token 必须是非空字符串');
2292
+ }
2293
+ const normalizedMethod = String(method).toUpperCase();
2294
+ const allowedMethods = ['GET', 'HEAD'];
2295
+ if (!allowedMethods.includes(normalizedMethod)) {
2296
+ throw new error_1.CloudBaseError('method 仅支持 GET 或 HEAD');
2297
+ }
2298
+ const { env } = this.getStorageConfig();
2299
+ const envId = envIdOverride || env;
2300
+ const encodedBucketId = encodeURIComponent(bucketId);
2301
+ const encodedObjectName = objectName
2302
+ .split('/')
2303
+ .map(seg => encodeURIComponent(seg))
2304
+ .join('/');
2305
+ let url = `https://${envId}.api.tcloudbasegateway.com/v1/storages/object/sign/${encodedBucketId}/${encodedObjectName}?token=${encodeURIComponent(token)}`;
2306
+ if (download !== undefined) {
2307
+ if (typeof download !== 'string') {
2308
+ throw new error_1.CloudBaseError('download 必须是字符串');
2309
+ }
2310
+ url += `&download=${encodeURIComponent(download)}`;
2311
+ }
2312
+ const { proxy } = this.environment.getAuthConfig();
2313
+ const res = await (0, utils_1.fetchStream)(url, {
2314
+ method: normalizedMethod
2315
+ }, proxy);
2316
+ if (!res.ok) {
2317
+ const text = await res.text();
2318
+ let payload = null;
2319
+ if (text) {
2320
+ try {
2321
+ payload = JSON.parse(text);
2322
+ }
2323
+ catch (_a) {
2324
+ throw new error_1.CloudBaseError(`HTTP_${res.status}: ${text}`);
2325
+ }
2326
+ }
2327
+ const code = (payload === null || payload === void 0 ? void 0 : payload.Code) || (payload === null || payload === void 0 ? void 0 : payload.code) || `HTTP_${res.status}`;
2328
+ const message = (payload === null || payload === void 0 ? void 0 : payload.Message) || (payload === null || payload === void 0 ? void 0 : payload.message) || res.statusText || '通过签名 URL 下载对象失败';
2329
+ throw new error_1.CloudBaseError(`${code}: ${message}`, {
2330
+ code,
2331
+ requestId: (payload === null || payload === void 0 ? void 0 : payload.RequestId) || (payload === null || payload === void 0 ? void 0 : payload.requestId)
2332
+ });
2333
+ }
2334
+ const responseHeaders = {};
2335
+ res.headers.forEach((value, key) => {
2336
+ responseHeaders[key] = value;
2337
+ });
2338
+ return {
2339
+ status: res.status,
2340
+ headers: responseHeaders,
2341
+ body: normalizedMethod === 'HEAD' ? null : res.body
2342
+ };
2343
+ }
2344
+ /**
2345
+ * 通过 CloudBase HTTP API 获取对象元信息(需认证)
2346
+ * GET/HEAD /v1/storages/object/info/authenticated/:bucketId/*objectName
2347
+ */
2348
+ async getObjectInfoAuthenticated(options) {
2349
+ const { bucketId, objectName, method = 'GET', accessToken, envId: envIdOverride } = options;
2350
+ if (!bucketId || typeof bucketId !== 'string') {
2351
+ throw new error_1.CloudBaseError('bucketId 必须是非空字符串');
2352
+ }
2353
+ if (!objectName || typeof objectName !== 'string') {
2354
+ throw new error_1.CloudBaseError('objectName 必须是非空字符串');
2355
+ }
2356
+ if (!accessToken || typeof accessToken !== 'string') {
2357
+ throw new error_1.CloudBaseError('accessToken 必须是非空字符串');
2358
+ }
2359
+ const normalizedMethod = String(method).toUpperCase();
2360
+ const allowedMethods = ['GET', 'HEAD'];
2361
+ if (!allowedMethods.includes(normalizedMethod)) {
2362
+ throw new error_1.CloudBaseError('method 仅支持 GET 或 HEAD');
2363
+ }
2364
+ const { env } = this.getStorageConfig();
2365
+ const envId = envIdOverride || env;
2366
+ const encodedBucketId = encodeURIComponent(bucketId);
2367
+ const encodedObjectName = objectName
2368
+ .split('/')
2369
+ .map(seg => encodeURIComponent(seg))
2370
+ .join('/');
2371
+ const url = `https://${envId}.api.tcloudbasegateway.com/v1/storages/object/info/authenticated/${encodedBucketId}/${encodedObjectName}`;
2372
+ const { proxy } = this.environment.getAuthConfig();
2373
+ const res = await (0, utils_1.fetchStream)(url, {
2374
+ method: normalizedMethod,
2375
+ headers: {
2376
+ Authorization: `Bearer ${accessToken}`
2377
+ }
2378
+ }, proxy);
2379
+ if (!res.ok) {
2380
+ const text = await res.text();
2381
+ let payload = null;
2382
+ if (text) {
2383
+ try {
2384
+ payload = JSON.parse(text);
2385
+ }
2386
+ catch (_a) {
2387
+ throw new error_1.CloudBaseError(`HTTP_${res.status}: ${text}`);
2388
+ }
2389
+ }
2390
+ const code = (payload === null || payload === void 0 ? void 0 : payload.Code) || (payload === null || payload === void 0 ? void 0 : payload.code) || `HTTP_${res.status}`;
2391
+ const message = (payload === null || payload === void 0 ? void 0 : payload.Message) || (payload === null || payload === void 0 ? void 0 : payload.message) || res.statusText || '获取对象元信息失败';
2392
+ throw new error_1.CloudBaseError(`${code}: ${message}`, {
2393
+ code,
2394
+ requestId: (payload === null || payload === void 0 ? void 0 : payload.RequestId) || (payload === null || payload === void 0 ? void 0 : payload.requestId)
2395
+ });
2396
+ }
2397
+ const responseHeaders = {};
2398
+ res.headers.forEach((value, key) => {
2399
+ responseHeaders[key] = value;
2400
+ });
2401
+ if (normalizedMethod === 'HEAD') {
2402
+ return {
2403
+ status: res.status,
2404
+ headers: responseHeaders,
2405
+ body: null
2406
+ };
2407
+ }
2408
+ const text = await res.text();
2409
+ let payload = null;
2410
+ if (text) {
2411
+ try {
2412
+ payload = JSON.parse(text);
2413
+ }
2414
+ catch (_b) {
2415
+ throw new error_1.CloudBaseError(`响应不是合法 JSON: ${text}`);
2416
+ }
2417
+ }
2418
+ if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
2419
+ throw new error_1.CloudBaseError('获取对象元信息成功但响应格式异常:期望 JSON 对象');
2420
+ }
2421
+ return {
2422
+ status: res.status,
2423
+ headers: responseHeaders,
2424
+ body: payload
2425
+ };
2426
+ }
2427
+ /**
2428
+ * 通过 CloudBase HTTP API 获取公开对象元信息(无需认证)
2429
+ * GET /v1/storages/object/info/public/:bucketId/*objectName
2430
+ */
2431
+ async getObjectInfoPublic(options) {
2432
+ const { bucketId, objectName, envId: envIdOverride } = options;
2433
+ if (!bucketId || typeof bucketId !== 'string') {
2434
+ throw new error_1.CloudBaseError('bucketId 必须是非空字符串');
2435
+ }
2436
+ if (!objectName || typeof objectName !== 'string') {
2437
+ throw new error_1.CloudBaseError('objectName 必须是非空字符串');
2438
+ }
2439
+ const { env } = this.getStorageConfig();
2440
+ const envId = envIdOverride || env;
2441
+ const encodedBucketId = encodeURIComponent(bucketId);
2442
+ const encodedObjectName = objectName
2443
+ .split('/')
2444
+ .map(seg => encodeURIComponent(seg))
2445
+ .join('/');
2446
+ const url = `https://${envId}.api.tcloudbasegateway.com/v1/storages/object/info/public/${encodedBucketId}/${encodedObjectName}`;
2447
+ const { proxy } = this.environment.getAuthConfig();
2448
+ const res = await (0, utils_1.fetchStream)(url, {
2449
+ method: 'GET'
2450
+ }, proxy);
2451
+ if (!res.ok) {
2452
+ const text = await res.text();
2453
+ let payload = null;
2454
+ if (text) {
2455
+ try {
2456
+ payload = JSON.parse(text);
2457
+ }
2458
+ catch (_a) {
2459
+ throw new error_1.CloudBaseError(`HTTP_${res.status}: ${text}`);
2460
+ }
2461
+ }
2462
+ const code = (payload === null || payload === void 0 ? void 0 : payload.Code) || (payload === null || payload === void 0 ? void 0 : payload.code) || `HTTP_${res.status}`;
2463
+ const message = (payload === null || payload === void 0 ? void 0 : payload.Message) || (payload === null || payload === void 0 ? void 0 : payload.message) || res.statusText || '获取公开对象元信息失败';
2464
+ throw new error_1.CloudBaseError(`${code}: ${message}`, {
2465
+ code,
2466
+ requestId: (payload === null || payload === void 0 ? void 0 : payload.RequestId) || (payload === null || payload === void 0 ? void 0 : payload.requestId)
2467
+ });
2468
+ }
2469
+ const responseHeaders = {};
2470
+ res.headers.forEach((value, key) => {
2471
+ responseHeaders[key] = value;
2472
+ });
2473
+ const text = await res.text();
2474
+ let payload = null;
2475
+ if (text) {
2476
+ try {
2477
+ payload = JSON.parse(text);
2478
+ }
2479
+ catch (_b) {
2480
+ throw new error_1.CloudBaseError(`响应不是合法 JSON: ${text}`);
2481
+ }
2482
+ }
2483
+ if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
2484
+ throw new error_1.CloudBaseError('获取公开对象元信息成功但响应格式异常:期望 JSON 对象');
2485
+ }
2486
+ return {
2487
+ status: res.status,
2488
+ headers: responseHeaders,
2489
+ body: payload
2490
+ };
2491
+ }
1261
2492
  /**
1262
2493
  * 获取 COS 配置
1263
2494
  */
@@ -1494,3 +2725,51 @@ __decorate([
1494
2725
  __decorate([
1495
2726
  (0, utils_1.preLazy)()
1496
2727
  ], StorageService.prototype, "searchFiles", null);
2728
+ __decorate([
2729
+ (0, utils_1.preLazy)()
2730
+ ], StorageService.prototype, "uploadObject", null);
2731
+ __decorate([
2732
+ (0, utils_1.preLazy)()
2733
+ ], StorageService.prototype, "deleteObject", null);
2734
+ __decorate([
2735
+ (0, utils_1.preLazy)()
2736
+ ], StorageService.prototype, "deleteObjects", null);
2737
+ __decorate([
2738
+ (0, utils_1.preLazy)()
2739
+ ], StorageService.prototype, "copyObject", null);
2740
+ __decorate([
2741
+ (0, utils_1.preLazy)()
2742
+ ], StorageService.prototype, "moveObject", null);
2743
+ __decorate([
2744
+ (0, utils_1.preLazy)()
2745
+ ], StorageService.prototype, "listObjects", null);
2746
+ __decorate([
2747
+ (0, utils_1.preLazy)()
2748
+ ], StorageService.prototype, "signObject", null);
2749
+ __decorate([
2750
+ (0, utils_1.preLazy)()
2751
+ ], StorageService.prototype, "signObjects", null);
2752
+ __decorate([
2753
+ (0, utils_1.preLazy)()
2754
+ ], StorageService.prototype, "signUploadObject", null);
2755
+ __decorate([
2756
+ (0, utils_1.preLazy)()
2757
+ ], StorageService.prototype, "uploadObjectBySign", null);
2758
+ __decorate([
2759
+ (0, utils_1.preLazy)()
2760
+ ], StorageService.prototype, "downloadAuthenticatedObject", null);
2761
+ __decorate([
2762
+ (0, utils_1.preLazy)()
2763
+ ], StorageService.prototype, "downloadObject", null);
2764
+ __decorate([
2765
+ (0, utils_1.preLazy)()
2766
+ ], StorageService.prototype, "downloadPublicObject", null);
2767
+ __decorate([
2768
+ (0, utils_1.preLazy)()
2769
+ ], StorageService.prototype, "downloadObjectBySign", null);
2770
+ __decorate([
2771
+ (0, utils_1.preLazy)()
2772
+ ], StorageService.prototype, "getObjectInfoAuthenticated", null);
2773
+ __decorate([
2774
+ (0, utils_1.preLazy)()
2775
+ ], StorageService.prototype, "getObjectInfoPublic", null);