Skip to content

Commit 586b43a

Browse files
authored
Dev/1.6.0 (#202)
feat 1、提升安全性:不再支持path-style 2、修复 uploadFile 未对参数 Key 进行校验
1 parent 0271282 commit 586b43a

File tree

10 files changed

+349
-146
lines changed

10 files changed

+349
-146
lines changed

demo/nextjs/package.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"name": "cos-js-sdk-nextjs",
3+
"version": "1.0.0",
4+
"description": "",
5+
"main": "index.js",
6+
"scripts": {
7+
"dev": "next",
8+
"build": "next build",
9+
"start": "next start",
10+
"test": "echo \"Error: no test specified\" && exit 1"
11+
},
12+
"author": "",
13+
"license": "ISC",
14+
"dependencies": {
15+
"cos-js-sdk-v5": "^1.5.0",
16+
"next": "^14.0.4",
17+
"react": "^18.2.0",
18+
"react-dom": "^18.2.0"
19+
}
20+
}

demo/nextjs/pages/index.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import COS from 'cos-js-sdk-v5';
2+
3+
// 初始化可参考 https://cloud.tencent.com/document/product/436/11459#.E5.BC.80.E5.A7.8B.E4.BD.BF.E7.94.A8
4+
const cos = new COS({
5+
SecretId: '',
6+
SecretKey: '',
7+
});
8+
9+
let selectedFile = null;
10+
11+
const handleFileChange = (event) => {
12+
selectedFile = event.target.files[0];
13+
};
14+
15+
const handleUpload = () => {
16+
if (selectedFile) {
17+
console.log('上传文件:', selectedFile);
18+
cos.uploadFile({
19+
Bucket: 'examplebucket-1250000000', /* 填入您自己的存储桶,必须字段 */
20+
Region: 'COS_REGION', /* 存储桶所在地域,例如ap-beijing,必须字段 */
21+
Key: selectedFile.name, /* 存储在桶里的对象键(例如1.jpg,a/b/test.txt),必须字段 */
22+
Body: selectedFile, /* 必须,上传文件对象,可以是input[type="file"]标签选择本地文件后得到的file对象 */
23+
SliceSize: 1024 * 1024 * 5, /* 触发分块上传的阈值,超过5MB使用分块上传,非必须 */
24+
onProgress: function (progressData) { /* 非必须 */
25+
console.log(JSON.stringify(progressData));
26+
},
27+
}, function (err, data) {
28+
console.log(err || data);
29+
});
30+
31+
} else {
32+
console.log('请选择一个文件');
33+
}
34+
};
35+
36+
export default () => <div>
37+
<input type="file" onChange={handleFileChange} />
38+
<button onClick={handleUpload}>上传</button>
39+
</div>

dist/cos-js-sdk-v5.js

Lines changed: 82 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8628,7 +8628,7 @@ module.exports = function(module) {
86288628
/*! exports provided: name, version, description, main, types, scripts, repository, keywords, author, license, bugs, homepage, dependencies, devDependencies, default */
86298629
/***/ (function(module) {
86308630

8631-
module.exports = JSON.parse("{\"name\":\"cos-js-sdk-v5\",\"version\":\"1.5.0\",\"description\":\"JavaScript SDK for [腾讯云对象存储](https://cloud.tencent.com/product/cos)\",\"main\":\"dist/cos-js-sdk-v5.js\",\"types\":\"index.d.ts\",\"scripts\":{\"prettier\":\"prettier --write src demo/demo.js demo/CIDemos/*.js test/test.js server/sts.js index.d.ts\",\"server\":\"node server/sts.js\",\"dev\":\"cross-env NODE_ENV=development webpack -w --mode=development\",\"build\":\"cross-env NODE_ENV=production webpack --mode=production\",\"cos-auth.min.js\":\"uglifyjs ./demo/common/cos-auth.js -o ./demo/common/cos-auth.min.js -c -m\",\"test\":\"jest --runInBand --coverage\"},\"repository\":{\"type\":\"git\",\"url\":\"git+https://github.com/tencentyun/cos-js-sdk-v5.git\"},\"keywords\":[],\"author\":\"carsonxu\",\"license\":\"ISC\",\"bugs\":{\"url\":\"https://github.com/tencentyun/cos-js-sdk-v5/issues\"},\"homepage\":\"https://github.com/tencentyun/cos-js-sdk-v5#readme\",\"dependencies\":{\"@xmldom/xmldom\":\"^0.8.6\"},\"devDependencies\":{\"@babel/core\":\"7.17.9\",\"@babel/plugin-transform-runtime\":\"7.18.10\",\"@babel/preset-env\":\"7.16.11\",\"babel-loader\":\"8.2.5\",\"body-parser\":\"^1.18.3\",\"cross-env\":\"^5.2.0\",\"express\":\"^4.16.4\",\"jest\":\"^29.3.1\",\"jest-environment-jsdom\":\"^29.3.1\",\"prettier\":\"^3.0.1\",\"qcloud-cos-sts\":\"^3.0.2\",\"request\":\"^2.87.0\",\"terser-webpack-plugin\":\"4.2.3\",\"uglifyjs\":\"^2.4.11\",\"webpack\":\"4.46.0\",\"webpack-cli\":\"4.10.0\"}}");
8631+
module.exports = JSON.parse("{\"name\":\"cos-js-sdk-v5\",\"version\":\"1.6.0\",\"description\":\"JavaScript SDK for [腾讯云对象存储](https://cloud.tencent.com/product/cos)\",\"main\":\"dist/cos-js-sdk-v5.js\",\"types\":\"index.d.ts\",\"scripts\":{\"prettier\":\"prettier --write src demo/demo.js demo/CIDemos/*.js test/test.js server/sts.js lib/request.js index.d.ts\",\"server\":\"node server/sts.js\",\"dev\":\"cross-env NODE_ENV=development webpack -w --mode=development\",\"build\":\"cross-env NODE_ENV=production webpack --mode=production\",\"cos-auth.min.js\":\"uglifyjs ./demo/common/cos-auth.js -o ./demo/common/cos-auth.min.js -c -m\",\"test\":\"jest --runInBand --coverage\"},\"repository\":{\"type\":\"git\",\"url\":\"git+https://github.com/tencentyun/cos-js-sdk-v5.git\"},\"keywords\":[],\"author\":\"carsonxu\",\"license\":\"ISC\",\"bugs\":{\"url\":\"https://github.com/tencentyun/cos-js-sdk-v5/issues\"},\"homepage\":\"https://github.com/tencentyun/cos-js-sdk-v5#readme\",\"dependencies\":{\"@xmldom/xmldom\":\"^0.8.6\"},\"devDependencies\":{\"@babel/core\":\"7.17.9\",\"@babel/plugin-transform-runtime\":\"7.18.10\",\"@babel/preset-env\":\"7.16.11\",\"babel-loader\":\"8.2.5\",\"body-parser\":\"^1.18.3\",\"cross-env\":\"^5.2.0\",\"express\":\"^4.16.4\",\"jest\":\"^29.3.1\",\"jest-environment-jsdom\":\"^29.3.1\",\"prettier\":\"^3.0.1\",\"qcloud-cos-sts\":\"^3.0.2\",\"request\":\"^2.87.0\",\"terser-webpack-plugin\":\"4.2.3\",\"uglifyjs\":\"^2.4.11\",\"webpack\":\"4.46.0\",\"webpack-cli\":\"4.10.0\"}}");
86328632

86338633
/***/ }),
86348634

@@ -12780,7 +12780,7 @@ function multipartComplete(params, callback) {
1278012780
Part: Parts
1278112781
}
1278212782
});
12783-
// CSP/ceph CompleteMultipartUpload 接口 body 写死了限制 1MB,这里醉倒 10000 片时,xml 字符串去掉空格853KB
12783+
// CSP/ceph CompleteMultipartUpload 接口 body 写死了限制 1MB,这里最多 10000 片时,xml 字符串去掉空格853KB
1278412784
xml = xml.replace(/\n\s*/g, '');
1278512785
var headers = params.Headers;
1278612786
headers['Content-Type'] = 'application/xml';
@@ -13406,7 +13406,8 @@ function getAuthorizationAsync(params, callback) {
1340613406
SecurityToken: StsData.SecurityToken || StsData.XCosSecurityToken || '',
1340713407
Token: StsData.Token || '',
1340813408
ClientIP: StsData.ClientIP || '',
13409-
ClientUA: StsData.ClientUA || ''
13409+
ClientUA: StsData.ClientUA || '',
13410+
SignFrom: 'client'
1341013411
};
1341113412
cb(null, AuthData);
1341213413
};
@@ -13507,7 +13508,8 @@ function getAuthorizationAsync(params, callback) {
1350713508
});
1350813509
var AuthData = {
1350913510
Authorization: Authorization,
13510-
SecurityToken: self.options.SecurityToken || self.options.XCosSecurityToken
13511+
SecurityToken: self.options.SecurityToken || self.options.XCosSecurityToken,
13512+
SignFrom: 'client'
1351113513
};
1351213514
cb(null, AuthData);
1351313515
return AuthData;
@@ -13516,10 +13518,12 @@ function getAuthorizationAsync(params, callback) {
1351613518
return '';
1351713519
}
1351813520

13519-
// 调整时间偏差
13521+
// 判断当前请求出错时能否重试
1352013522
function allowRetry(err) {
13521-
var allowRetry = false;
13523+
var self = this;
13524+
var canRetry = false;
1352213525
var isTimeError = false;
13526+
var networkError = false;
1352313527
var serverDate = err.headers && (err.headers.date || err.headers.Date) || err.error && err.error.ServerTime;
1352413528
try {
1352513529
var errorCode = err.error.Code;
@@ -13529,18 +13533,46 @@ function allowRetry(err) {
1352913533
}
1353013534
} catch (e) {}
1353113535
if (err) {
13536+
// 调整时间偏差
1353213537
if (isTimeError && serverDate) {
1353313538
var serverTime = Date.parse(serverDate);
1353413539
if (this.options.CorrectClockSkew && Math.abs(util.getSkewTime(this.options.SystemClockOffset) - serverTime) >= 30000) {
1353513540
console.error('error: Local time is too skewed.');
1353613541
this.options.SystemClockOffset = serverTime - Date.now();
13537-
allowRetry = true;
13542+
canRetry = true;
1353813543
}
1353913544
} else if (Math.floor(err.statusCode / 100) === 5) {
13540-
allowRetry = true;
13545+
canRetry = true;
13546+
} else if (err.message === 'CORS blocked or network error') {
13547+
// 跨域/网络错误都包含在内
13548+
networkError = true;
13549+
canRetry = self.options.AutoSwitchHost;
1354113550
}
1354213551
}
13543-
return allowRetry;
13552+
return {
13553+
canRetry: canRetry,
13554+
networkError: networkError
13555+
};
13556+
}
13557+
13558+
/**
13559+
* requestUrl:请求的url,用于判断是否cos主域名,true才切
13560+
* clientCalcSign:是否客户端计算签名,服务端返回的签名不能切,true才切
13561+
* networkError:是否未知网络错误,true才切
13562+
* */
13563+
function canSwitchHost(_ref) {
13564+
var requestUrl = _ref.requestUrl,
13565+
clientCalcSign = _ref.clientCalcSign,
13566+
networkError = _ref.networkError;
13567+
if (!this.options.AutoSwitchHost) return false;
13568+
if (!requestUrl) return false;
13569+
if (!clientCalcSign) return false;
13570+
if (!networkError) return false;
13571+
var commonReg = /^https?:\/\/[^\/]*\.cos\.[^\/]*\.myqcloud\.com(\/.*)?$/;
13572+
var accelerateReg = /^https?:\/\/[^\/]*\.cos\.accelerate\.myqcloud\.com(\/.*)?$/;
13573+
// 当前域名是cos主域名才切换
13574+
var isCommonCosHost = commonReg.test(requestUrl) && !accelerateReg.test(requestUrl);
13575+
return isCommonCosHost;
1354413576
}
1354513577

1354613578
// 获取签名并发起请求
@@ -13560,6 +13592,11 @@ function submitRequest(params, callback) {
1356013592
params.qs && (params.qs = util.clearKey(params.qs));
1356113593
var Query = util.clone(params.qs);
1356213594
params.action && (Query[params.action] = '');
13595+
13596+
/**
13597+
* 手动传params.SignHost的场景:cos.getService、cos.getObjectUrl
13598+
* 手动传Url的场景:cos.request
13599+
*/
1356313600
var paramsUrl = params.url || params.Url;
1356413601
var SignHost = params.SignHost || getSignHost.call(this, {
1356513602
Bucket: params.Bucket,
@@ -13573,6 +13610,10 @@ function submitRequest(params, callback) {
1357313610
signStartTime: new Date().getTime(),
1357413611
retryTimes: tryTimes - 1
1357513612
});
13613+
if (params.SwitchHost) {
13614+
// 更换要签的host
13615+
SignHost = SignHost.replace(/myqcloud.com/, 'tencentcos.cn');
13616+
}
1357613617
getAuthorizationAsync.call(self, {
1357713618
Bucket: params.Bucket || '',
1357813619
Region: params.Region || '',
@@ -13584,7 +13625,8 @@ function submitRequest(params, callback) {
1358413625
Action: params.Action,
1358513626
ResourceKey: params.ResourceKey,
1358613627
Scope: params.Scope,
13587-
ForceSignHost: self.options.ForceSignHost
13628+
ForceSignHost: self.options.ForceSignHost,
13629+
SwitchHost: params.SwitchHost
1358813630
}, function (err, AuthData) {
1358913631
if (err) {
1359013632
callback(err);
@@ -13599,7 +13641,14 @@ function submitRequest(params, callback) {
1359913641
tracker && tracker.setParams({
1360013642
httpEndTime: new Date().getTime()
1360113643
});
13602-
if (err && tryTimes < 2 && (oldClockOffset !== self.options.SystemClockOffset || allowRetry.call(self, err))) {
13644+
var canRetry = false;
13645+
var networkError = false;
13646+
if (err) {
13647+
var info = allowRetry.call(self, err);
13648+
canRetry = info.canRetry || oldClockOffset !== self.options.SystemClockOffset;
13649+
networkError = info.networkError;
13650+
}
13651+
if (err && tryTimes < 2 && canRetry) {
1360313652
if (params.headers) {
1360413653
delete params.headers.Authorization;
1360513654
delete params.headers['token'];
@@ -13608,6 +13657,13 @@ function submitRequest(params, callback) {
1360813657
params.headers['x-cos-security-token'] && delete params.headers['x-cos-security-token'];
1360913658
params.headers['x-ci-security-token'] && delete params.headers['x-ci-security-token'];
1361013659
}
13660+
// 进入重试逻辑时 需判断是否需要切换cos备用域名
13661+
var switchHost = canSwitchHost.call(self, {
13662+
requestUrl: (err === null || err === void 0 ? void 0 : err.url) || '',
13663+
clientCalcSign: AuthData.SignFrom === 'client',
13664+
networkError: networkError
13665+
});
13666+
params.SwitchHost = switchHost;
1361113667
next(tryTimes + 1);
1361213668
} else {
1361313669
callback(err, data);
@@ -13643,6 +13699,10 @@ function _submitRequest(params, callback) {
1364313699
region: region,
1364413700
object: object
1364513701
});
13702+
if (params.SwitchHost) {
13703+
// 更换请求的url
13704+
url = url.replace(/myqcloud.com/, 'tencentcos.cn');
13705+
}
1364613706
if (params.action) {
1364713707
// 已知问题,某些版本的qq会对url自动拼接(比如/upload被拼接成/upload=(null))导致签名错误,这里做下兼容。
1364813708
url = url + '?' + (util.isIOS_QQ ? "".concat(params.action, "=") : params.action);
@@ -13745,6 +13805,8 @@ function _submitRequest(params, callback) {
1374513805
response && response.statusCode && (attrs.statusCode = response.statusCode);
1374613806
response && response.headers && (attrs.headers = response.headers);
1374713807
if (err) {
13808+
opt.url && (attrs.url = opt.url);
13809+
opt.method && (attrs.method = opt.method);
1374813810
err = util.extend(err || {}, attrs);
1374913811
callback(err, null);
1375013812
} else {
@@ -13982,7 +14044,9 @@ var defaultOptions = {
1398214044
// 上报时是否对每个分块上传做单独上报
1398314045
TrackerDelay: 5000,
1398414046
// 周期性上报,单位毫秒。0代表实时上报
13985-
CustomId: '' // 自定义上报id
14047+
CustomId: '',
14048+
// 自定义上报id
14049+
AutoSwitchHost: true
1398614050
};
1398714051

1398814052
// 对外暴露的类
@@ -14009,9 +14073,14 @@ var COS = function COS(options) {
1400914073
console.error('error: SecretKey format is incorrect. Please check');
1401014074
}
1401114075
if (util.isNode()) {
14076+
console.log('Tip: Next.js、Nuxt.js 等服务端渲染技术可正常使用JavaScript SDK,请忽略下方 nodejs 环境警告');
1401214077
console.warn('warning: cos-js-sdk-v5 不支持 nodejs 环境使用,请改用 cos-nodejs-sdk-v5,参考文档: https://cloud.tencent.com/document/product/436/8629');
1401314078
console.warn('warning: cos-js-sdk-v5 does not support nodejs environment. Please use cos-nodejs-sdk-v5 instead. See: https://cloud.tencent.com/document/product/436/8629');
1401414079
}
14080+
if (this.options.ForcePathStyle) {
14081+
console.warn('cos-js-sdk-v5不再支持使用path-style,仅支持使用virtual-hosted-style,参考文档:https://cloud.tencent.com/document/product/436/96243');
14082+
throw new Error('ForcePathStyle is not supported');
14083+
}
1401514084
event.init(this);
1401614085
task.init(this);
1401714086
};
@@ -15270,7 +15339,7 @@ var hasMissingParams = function hasMissingParams(apiName, params) {
1527015339
if (apiName.indexOf('Bucket') > -1 || apiName === 'deleteMultipleObject' || apiName === 'multipartList' || apiName === 'listObjectVersions') {
1527115340
if (checkBucket && !Bucket) return 'Bucket';
1527215341
if (checkRegion && !Region) return 'Region';
15273-
} else if (apiName.indexOf('Object') > -1 || apiName.indexOf('multipart') > -1 || apiName === 'sliceUploadFile' || apiName === 'abortUploadTask') {
15342+
} else if (apiName.indexOf('Object') > -1 || apiName.indexOf('multipart') > -1 || apiName === 'sliceUploadFile' || apiName === 'abortUploadTask' || apiName === 'uploadFile') {
1527415343
if (checkBucket && !Bucket) return 'Bucket';
1527515344
if (checkRegion && !Region) return 'Region';
1527615345
if (!Key) return 'Key';

dist/cos-js-sdk-v5.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

index.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ declare namespace COS {
191191
DeepTracker?: boolean;
192192
TrackerDelay?: number;
193193
CustomId?: string;
194+
AutoSwitchHost?: boolean;
194195
/** 链路上报 */
195196
/** 获取签名的回调方法,如果没有 SecretId、SecretKey 时,必选 */
196197
getAuthorization?: (
@@ -298,6 +299,10 @@ declare namespace COS {
298299
message: string;
299300
/** 兼容老的错误信息字段,不建议使用,可能是参数错误、客户端出错、或服务端返回的错误 */
300301
error: string | Error | { Code: string; Message: string };
302+
/** 当前请求的Url */
303+
url: string;
304+
/** 当前请求的method */
305+
method: string;
301306
}
302307
/** 回调的错误格式,其中服务端返回错误码可查看 @see https://cloud.tencent.com/document/product/436/7730 */
303308
type CosError = null | CosSdkError;

0 commit comments

Comments
 (0)