背景
原来使用的cos是调用的node接口,但是由于公司node项目的网关限制了上传文件大小,然后的然后就由前端直传cos了(主要是还是自己动手丰衣足食);
但是呢!前端直传cos使用固定密钥是非常不安全的,所以使用node封装一个返回临时密钥的接口,然后前端调用临时密钥再上传cos~
具体实现
1. 实现node接口
- 使用插件
request
、crypto
- 服务端使用固定密钥调用 STS 服务申请临时密钥(具体内容请参见文底参考文档)
- STS服务接入(参考:https://github.com/tencentyun/qcloud–cos–sts–sdk/blob/master/nodejs/sdk/sts.js)
- sts文件注意事项:
// sts.ts 文件
/* eslint-disable */
import * as request from 'request'
const crypto = require('crypto')
const StsUrl = 'https://{host}/'
const util = {
// 获取随机数
getRandom(min, max) {
return Math.round(Math.random() * (max - min) + min)
},
// obj 转 query string
json2str(obj, $notEncode = '') {
const arr: any = []
Object.keys(obj)
.sort()
.forEach(item => {
const val = obj[item] || ''
arr.push(`${item}=${$notEncode ? encodeURIComponent(val) : val}`)
})
return arr.join('&')
},
// 计算签名
getSignature(opt, key, method, stsDomain) {
const formatString = `${method + stsDomain}/?${util.json2str(opt)}`
const hmac = crypto.createHmac('sha1', key)
const sign = hmac.update(Buffer.from(formatString, 'utf8')).digest('base64')
return sign
},
// v2接口的key首字母小写,v3改成大写,此处做了向下兼容
backwardCompat(data) {
const compat:any = {}
for (const key in data) {
if (typeof data[key] === 'object') {
compat[this.lowerFirstLetter(key)] = this.backwardCompat(data[key])
} else if (key === 'Token') {
compat.sessionToken = data[key]
} else {
compat[this.lowerFirstLetter(key)] = data[key]
}
}
return compat
},
lowerFirstLetter(source) {
return source.charAt(0).toLowerCase() + source.slice(1)
},
}
// 拼接获取临时密钥的参数
const getTempCredential = function (options, callback) {
if (options?.durationInSeconds !== undefined) {
console.warn('warning: durationInSeconds has been deprecated, Please use durationSeconds ).')
}
const secretId = options?.secretId
const secretKey = options?.secretKey
const proxy = options?.proxy || ''
const region = options?.region || 'ap-beijing'
const durationSeconds = options?.durationSeconds || options?.durationInSeconds || 1800
const policy = options?.policy
const endpoint = 'sts.tencentcloudapi.com'
const policyStr = JSON.stringify(policy)
const action = options?.action || 'GetFederationToken'
const nonce = util.getRandom(10000, 20000)
const timestamp = parseInt(`${+new Date() / 1000}`) // eslint-disable-line no-undef
const method = 'POST'
const name = 'cos-sts-nodejs' // 临时会话名称
const params: any = {
SecretId: secretId,
Timestamp: timestamp,
Nonce: nonce,
Action: action,
DurationSeconds: durationSeconds,
Version: '2018-08-13',
Region: region,
Policy: encodeURIComponent(policyStr),
}
if (action === 'AssumeRole') {
params.RoleSessionName = name
params.RoleArn = options?.roleArn
} else {
params.Name = name
}
params.Signature = util.getSignature(params, secretKey, method, endpoint)
const opt = {
method,
url: StsUrl.replace('{host}', endpoint),
strictSSL: false,
json: true,
form: params,
headers: {
Host: endpoint,
},
proxy,
}
request(opt, (err, response, body) => {
let data = body.Response
if (data) {
if (data.Error) {
callback(data.Error)
} else {
try {
data.startTime = data.ExpiredTime - durationSeconds
data = util.backwardCompat(data)
callback(null, data)
} catch (e) {
callback(new Error(`Parse Response Error: ${JSON.stringify(data)}`))
}
}
} else {
callback(err || body)
}
})
}
// 获取联合身份临时访问凭证 GetFederationToken
const getCredential = (opt, callback) => {
Object.assign(opt, { action: 'GetFederationToken' })
if (callback) return getTempCredential(opt, callback)
return new Promise((resolve, reject) => {
getTempCredential(opt, (err, data) => {
err ? reject(err) : resolve(data)
})
})
}
}
const STS = {
getCredential,
}
export default STS
async getTempCosKeyId() {
const secretKeyId = await getSecretKeyId();
// 配置参数
const config = {
secretId: secretKeyId.secretId, // 固定密钥
secretKey: secretKeyId.secretKey, // 固定密钥
proxy: "",
host: "sts.tencentcloudapi.com", // 域名,非必须,默认为 sts.tencentcloudapi.com
durationSeconds: 1800, // 密钥有效期
// 放行判断相关参数
bucket: "bucket", // 换成你的 bucket
region: "region", // 换成 bucket 所在地区
allowPrefix: "/web", // 上传文件前缀,可自定义为惯用前缀,
};
const bucket = config.bucket || "";
const shortBucketName = bucket.slice(0, bucket.lastIndexOf("-"));
const appId = bucket.slice(1 + bucket.lastIndexOf("-"));
const policy = {
version: "2.0",
statement: [
{
action: [
// 简单上传
"name/cos:PutObject",
"name/cos:PostObject",
// 分片上传
"name/cos:InitiateMultipartUpload",
"name/cos:ListMultipartUploads",
"name/cos:ListParts",
"name/cos:UploadPart",
"name/cos:CompleteMultipartUpload",
],
effect: "allow",
principal: { qcs: ["*"] },
resource: [
`qcs::cos:${config.region}:uid/${appId}:prefix//${appId}/${shortBucketName}/${config.allowPrefix}`,
],
},
],
};
// 返回接口
return new Promise((resolve, reject) => {
STS.getCredential(
{
secretId: config.secretId,
secretKey: config.secretKey,
proxy: config.proxy,
policy,
durationSeconds: config.durationSeconds,
},
(err, credential) => {
if (!err) {
resolve(credential);
console.log(err || credential);
} else {
reject(err);
}
}
);
}).catch((error) => error);
}
2. 前端封装cos直传组件
- 使用插件
cos-js-sdk-v5
- 将cos里面的密钥
{SecretId:,SecretKey}
换成获取临时密钥getAuthorization:(op,callback)=>{//接口获取临时密钥,执行callback}
- 上传组件封装
import { UPLOAD_BUCKET, UPLOAD_REGION, UPLOAD_PREFIX, } from '@/utils/globalData';
import { queryGetTempCosKeyId } from '@/services/upload' // 获取临时密钥接口
const COS = require('cos-js-sdk-v5');
/**
* @param {object} option
*/
export default function uploadCos(option) {
const cos = new COS({
// getAuthorization 必选参数
getAuthorization: function (options, callback) {
// 异步获取临时密钥
queryGetTempCosKeyId().then(res => {
const { credentials, expiredTime: ExpiredTime, startTime: StartTime } = res?.data?.result
callback({
TmpSecretId: credentials.tmpSecretId,
TmpSecretKey: credentials.tmpSecretKey,
SecurityToken: credentials.sessionToken,
// 建议返回服务器时间作为签名的开始时间,避免用户浏览器本地时间偏差过大导致签名错误
StartTime, // 时间戳,单位秒,如:1580000000
ExpiredTime, // 时间戳,单位秒,如:1580000000
});
})
}
});
const Bucket_Region_Config = {
Bucket: UPLOAD_BUCKET,
Region: UPLOAD_REGION,
};
const errFn = (err, data) => {
if (err) {
option.onError(err);
} else {
option.onSuccess(data);
}
}
const progressFn = (progressData) => {
if (!done) {
option.onProgress(progressData);
if (progressData.percent >= 1) {
done = true;
}
}
}
const { file = {}, Prefix = UPLOAD_PREFIX } = option;
let done = false;
const timestamp = new Date().getTime();
const newFileName = `${timestamp}_${file.name}`;
if (file.size > 1024 * 1024 * 20) { // 大于20m走分片上传
cos.sliceUploadFile(
{
...Bucket_Region_Config,
Key: (Prefix || '') + newFileName,
Body: file,
onProgress: (progressData) => progressFn(progressData)
},
(err, data) => errFn(err, data)
);
} else {
cos.putObject(
{
...Bucket_Region_Config,
Key: (Prefix || '') + newFileName,
Body: file,
onProgress: (progressData) => progressFn(progressData)
},
(err, data) => errFn(err, data)
);
}
return false;
}
3. 组件使用
import uploadCos from '@/utils/uploadCos'
const uploadAction = (val) => {
uploadCos({
file: val.file,
onSuccess: (completeData) => {
setUploadVal(val => {
console.log(`上传完成,文件为${completeData?.Location}`);
},
onError: (err) => {
message.error(`文件上传失败,请稍后重试!,${err}`)
},
});
}
参数
node获取临时密钥接口:https://github.com/tencentyun/qcloud-cos-sts-sdk/blob/master/nodejs/demo/sts-server.js
前端使用临时密钥:https://cloud.tencent.com/document/product/436/11459
cos文档:https://cloud.tencent.com/document/product/436/14048
原文地址:https://blog.csdn.net/weixin_44258964/article/details/129767691
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.7code.cn/show_44982.html
如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除!
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。