@cloudbase/manager-node 5.4.0-beta.1 → 5.4.0-beta.2
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/storage/index.js +102 -6
- package/package.json +3 -1
- package/types/storage/index.d.ts +4 -0
package/lib/storage/index.js
CHANGED
|
@@ -16,6 +16,7 @@ const path_1 = __importDefault(require("path"));
|
|
|
16
16
|
const make_dir_1 = __importDefault(require("make-dir"));
|
|
17
17
|
const walkdir_1 = __importDefault(require("walkdir"));
|
|
18
18
|
const micromatch_1 = __importDefault(require("micromatch"));
|
|
19
|
+
const mime_types_1 = __importDefault(require("mime-types"));
|
|
19
20
|
const cos_nodejs_sdk_v5_1 = __importDefault(require("cos-nodejs-sdk-v5"));
|
|
20
21
|
const utils_1 = require("../utils");
|
|
21
22
|
const error_1 = require("../error");
|
|
@@ -1314,11 +1315,18 @@ class StorageService {
|
|
|
1314
1315
|
code: 'FileSizeLimitExceeded'
|
|
1315
1316
|
});
|
|
1316
1317
|
}
|
|
1317
|
-
|
|
1318
|
+
const finalContentType = this.resolveUploadContentType({
|
|
1319
|
+
contentType,
|
|
1320
|
+
localPath,
|
|
1321
|
+
objectName,
|
|
1322
|
+
body
|
|
1323
|
+
});
|
|
1318
1324
|
const headers = {
|
|
1319
|
-
Authorization: `Bearer ${accessToken}
|
|
1320
|
-
'Content-Type': contentType || 'application/octet-stream'
|
|
1325
|
+
Authorization: `Bearer ${accessToken}`
|
|
1321
1326
|
};
|
|
1327
|
+
if (finalContentType) {
|
|
1328
|
+
headers['Content-Type'] = finalContentType;
|
|
1329
|
+
}
|
|
1322
1330
|
if (fileSize !== undefined) {
|
|
1323
1331
|
headers['Content-Length'] = String(fileSize);
|
|
1324
1332
|
}
|
|
@@ -1994,9 +2002,16 @@ class StorageService {
|
|
|
1994
2002
|
else if (Buffer.isBuffer(body) && fileSize === undefined) {
|
|
1995
2003
|
fileSize = body.length;
|
|
1996
2004
|
}
|
|
1997
|
-
const
|
|
1998
|
-
|
|
1999
|
-
|
|
2005
|
+
const finalContentType = this.resolveUploadContentType({
|
|
2006
|
+
contentType,
|
|
2007
|
+
localPath,
|
|
2008
|
+
objectName,
|
|
2009
|
+
body
|
|
2010
|
+
});
|
|
2011
|
+
const headers = {};
|
|
2012
|
+
if (finalContentType) {
|
|
2013
|
+
headers['Content-Type'] = finalContentType;
|
|
2014
|
+
}
|
|
2000
2015
|
if (fileSize !== undefined) {
|
|
2001
2016
|
headers['Content-Length'] = String(fileSize);
|
|
2002
2017
|
}
|
|
@@ -2636,6 +2651,87 @@ class StorageService {
|
|
|
2636
2651
|
res.map(item => /Error/gi.test(Object.prototype.toString.call(item)) && resultErrorArr.push(item));
|
|
2637
2652
|
return resultErrorArr;
|
|
2638
2653
|
}
|
|
2654
|
+
resolveUploadContentType(params) {
|
|
2655
|
+
const { contentType, localPath, objectName, body } = params;
|
|
2656
|
+
// 0. 用户显式指定优先
|
|
2657
|
+
const trimmedContentType = contentType === null || contentType === void 0 ? void 0 : contentType.trim();
|
|
2658
|
+
if (trimmedContentType) {
|
|
2659
|
+
return this.assertValidContentType(trimmedContentType);
|
|
2660
|
+
}
|
|
2661
|
+
// 1. 扩展名推断(优先)
|
|
2662
|
+
const byExt = this.inferMimeByExtension(localPath || objectName);
|
|
2663
|
+
if (byExt)
|
|
2664
|
+
return byExt;
|
|
2665
|
+
// 2. magic bytes 二次识别
|
|
2666
|
+
const byMagic = this.inferMimeByMagicBytes(localPath, body);
|
|
2667
|
+
if (byMagic)
|
|
2668
|
+
return byMagic;
|
|
2669
|
+
return undefined;
|
|
2670
|
+
}
|
|
2671
|
+
inferMimeByExtension(filePathOrName) {
|
|
2672
|
+
if (!filePathOrName)
|
|
2673
|
+
return undefined;
|
|
2674
|
+
const result = mime_types_1.default.lookup(filePathOrName);
|
|
2675
|
+
return typeof result === 'string' ? result : undefined;
|
|
2676
|
+
}
|
|
2677
|
+
inferMimeByMagicBytes(localPath, body) {
|
|
2678
|
+
let head;
|
|
2679
|
+
try {
|
|
2680
|
+
if (localPath) {
|
|
2681
|
+
const fd = fs_1.default.openSync(path_1.default.resolve(localPath), 'r');
|
|
2682
|
+
try {
|
|
2683
|
+
const buf = Buffer.alloc(512);
|
|
2684
|
+
const n = fs_1.default.readSync(fd, buf, 0, 512, 0);
|
|
2685
|
+
head = buf.slice(0, n);
|
|
2686
|
+
}
|
|
2687
|
+
finally {
|
|
2688
|
+
fs_1.default.closeSync(fd);
|
|
2689
|
+
}
|
|
2690
|
+
}
|
|
2691
|
+
else if (Buffer.isBuffer(body)) {
|
|
2692
|
+
head = body.slice(0, 512);
|
|
2693
|
+
}
|
|
2694
|
+
}
|
|
2695
|
+
catch (_a) {
|
|
2696
|
+
return undefined;
|
|
2697
|
+
}
|
|
2698
|
+
if (!head || head.length === 0)
|
|
2699
|
+
return undefined;
|
|
2700
|
+
const startsWith = (sig) => head.length >= sig.length && sig.every((b, i) => head[i] === b);
|
|
2701
|
+
if (startsWith([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]))
|
|
2702
|
+
return 'image/png';
|
|
2703
|
+
if (startsWith([0xFF, 0xD8, 0xFF]))
|
|
2704
|
+
return 'image/jpeg';
|
|
2705
|
+
if (head.slice(0, 6).toString('ascii') === 'GIF87a' || head.slice(0, 6).toString('ascii') === 'GIF89a')
|
|
2706
|
+
return 'image/gif';
|
|
2707
|
+
if (head.slice(0, 4).toString('ascii') === 'RIFF' && head.slice(8, 12).toString('ascii') === 'WEBP')
|
|
2708
|
+
return 'image/webp';
|
|
2709
|
+
if (head.slice(0, 4).toString('ascii') === '%PDF')
|
|
2710
|
+
return 'application/pdf';
|
|
2711
|
+
if (startsWith([0x50, 0x4B, 0x03, 0x04]))
|
|
2712
|
+
return 'application/zip';
|
|
2713
|
+
if (startsWith([0x1F, 0x8B]))
|
|
2714
|
+
return 'application/gzip';
|
|
2715
|
+
if (head.length >= 262 && head.slice(257, 262).toString('ascii') === 'ustar')
|
|
2716
|
+
return 'application/x-tar';
|
|
2717
|
+
return undefined;
|
|
2718
|
+
}
|
|
2719
|
+
assertValidContentType(input) {
|
|
2720
|
+
const ct = input.trim();
|
|
2721
|
+
if (!ct)
|
|
2722
|
+
throw new error_1.CloudBaseError('contentType 不能为空');
|
|
2723
|
+
// 防 header 注入 / 控制字符
|
|
2724
|
+
if (/[\u0000-\u001F\u007F]/.test(ct)) {
|
|
2725
|
+
throw new error_1.CloudBaseError('contentType 非法:包含控制字符');
|
|
2726
|
+
}
|
|
2727
|
+
// 只校验主类型(; 后参数放宽)
|
|
2728
|
+
const base = ct.split(';', 1)[0].trim();
|
|
2729
|
+
const typeSubtypeRe = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+\/[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
|
|
2730
|
+
if (!typeSubtypeRe.test(base)) {
|
|
2731
|
+
throw new error_1.CloudBaseError('contentType 非法:主类型格式不正确');
|
|
2732
|
+
}
|
|
2733
|
+
return ct;
|
|
2734
|
+
}
|
|
2639
2735
|
}
|
|
2640
2736
|
exports.StorageService = StorageService;
|
|
2641
2737
|
__decorate([
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloudbase/manager-node",
|
|
3
|
-
"version": "5.4.0-beta.
|
|
3
|
+
"version": "5.4.0-beta.2",
|
|
4
4
|
"description": "The node manage service api for cloudbase.",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"@lerna/create-symlink": "^6.4.1",
|
|
26
26
|
"@types/fs-extra": "^11.0.4",
|
|
27
27
|
"@types/jest": "^24.0.18",
|
|
28
|
+
"@types/mime-types": "^3.0.1",
|
|
28
29
|
"@types/node": "^12.7.4",
|
|
29
30
|
"@types/node-fetch": "^2.5.0",
|
|
30
31
|
"@types/unzipper": "^0.10.11",
|
|
@@ -49,6 +50,7 @@
|
|
|
49
50
|
"lodash": "^4.17.21",
|
|
50
51
|
"make-dir": "^3.0.0",
|
|
51
52
|
"micromatch": "^4.0.2",
|
|
53
|
+
"mime-types": "^3.0.2",
|
|
52
54
|
"node-fetch": "^2.6.0",
|
|
53
55
|
"query-string": "^6.8.3",
|
|
54
56
|
"unzipper": "^0.12.3",
|
package/types/storage/index.d.ts
CHANGED
|
@@ -569,5 +569,9 @@ export declare class StorageService {
|
|
|
569
569
|
* @returns
|
|
570
570
|
*/
|
|
571
571
|
private determineDownLoadResultIsError;
|
|
572
|
+
private resolveUploadContentType;
|
|
573
|
+
private inferMimeByExtension;
|
|
574
|
+
private inferMimeByMagicBytes;
|
|
575
|
+
private assertValidContentType;
|
|
572
576
|
}
|
|
573
577
|
export {};
|