node.js使用腾讯云上传图片,解决跨域和403 Forbidden Access Denied问题

January 07,2022

1. 注册腾讯云

再注册一个子账户,用子账户登录

2. 开通cos服务

image.png

3. 创建存储桶

image.png

生成子账户密钥

点击新建密钥生成密钥

image.png

4. 获取上传需要的临时密钥

https://cloud.tencent.com/document/product/436/14048

4.1 在node项目中安装qcloud-cos-sts

npm i qcloud-cos-sts --save
app.js 设置允许跨域访问和路由

...
// 支持跨域访问
app.all('*', function (req, res, next) {
    res.header('Content-Type', 'application/json');
    res.header('Access-Control-Allow-Origin', 'http://127.0.0.1');
    res.header('Access-Control-Allow-Headers', 'origin,accept,content-type');
    if (req.method.toUpperCase() === 'OPTIONS') {
        res.end();
    } else {
        next();
    }
});

//路由
app.get('/tx/sts', stsController.getCredential)

________________________________

stsController.js

var STS = require('qcloud-cos-sts');
// 配置参数
var config = {
    secretId: 'AKI**********************IDq', //控制台获取到的secretId
    secretKey: 'NUJ**********************wQdZ', //控制台获取到的secretKey
    proxy: '',
    durationSeconds: 1800,

    // 放行判断相关参数
    bucket: 'fr****-125******2', // 存储桶名称
    region: 'ap-shanghai', // 所属地域
    allowPrefix: 'exampleobject', // 这里改成允许的路径前缀,可以根据自己网站的用户登录态判断允许上传的具体路径,例子: a.jpg 或者 a/* 或者 * (使用通配符*存在重大安全风险, 请谨慎评估使用)
    // 简单上传和分片,需要以下的权限,其他权限列表请看 https://cloud.tencent.com/document/product/436/31923
    allowActions: [
        // 简单上传
        'name/cos:PutObject',
        'name/cos:PostObject',
        // 分片上传
        'name/cos:InitiateMultipartUpload',
        'name/cos:ListMultipartUploads',
        'name/cos:ListParts',
        'name/cos:UploadPart',
        'name/cos:CompleteMultipartUpload'
    ],
};

module.exports={
    getCredential: (req,res)=>{
        var shortBucketName = config.bucket.substr(0 , config.bucket.lastIndexOf('-'));
        var appId = config.bucket.substr(1 + config.bucket.lastIndexOf('-'));
        var policy = {
            'version': '2.0',
            'statement': [{
                'action': config.allowActions,
                'effect': 'allow',
                'principal': {'qcs': ['*']},
                'resource': [
                    'qcs::cos:' + config.region + ':uid/' + appId + ':prefix//' + appId + '/' + shortBucketName + '/' + config.allowPrefix,
                ],
            }],
        };
        STS.getCredential({
            secretId: config.secretId,
            secretKey: config.secretKey,
            proxy: config.proxy,
            durationSeconds: config.durationSeconds,
            policy: policy,
        }, function (err, tempKeys) {
            var result = JSON.stringify(err || tempKeys) || '';
            console.log('result: ',result)
            res.send(result)
        });
    }
}
______________________________________
cos.html

<!DOCTYPE html>
<html lang="zh_CN">
    <head>
        <meta charset="UTF-8">
        <script src="https://cdn.jsdelivr.net/npm/cos-js-sdk-v5/dist/cos-js-sdk-v5.min.js"></script>
    </head>
    <body>
        <input type="file" onchange="uploadImg()">
        <script>
        var Bucket = 'f****-*******32';  /* 存储桶,必须字段 */
        var Region = 'ap-shanghai';     /* 存储桶所在地域,必须字段 */
        
        // 初始化实例
        var cos = new COS({
            // getAuthorization 必选参数
            getAuthorization: function (options, callback) {
                var url = 'http://127.0.0.1:3000/tx/sts'; // url替换成您自己的后端服务
                var xhr = new XMLHttpRequest();
                xhr.open('GET', url, true);
                xhr.onload = function (e) {
                    try {
                        var data = JSON.parse(e.target.responseText);
                        var credentials = data.credentials;
                    } catch (e) {
                    }
                    if (!data || !credentials) {
                      return console.error('credentials invalid:\n' + JSON.stringify(data, null, 2))
                    };
                    let obj = {
                      TmpSecretId: credentials.tmpSecretId,
                      TmpSecretKey: credentials.tmpSecretKey,
                      SecurityToken: credentials.sessionToken,
                      // 建议返回服务器时间作为签名的开始时间,避免用户浏览器本地时间偏差过大导致签名错误
                      StartTime: data.startTime, // 时间戳,单位秒,如:1580000000
                      ExpiredTime: data.expiredTime, // 时间戳,单位秒,如:1580000000
                    }
                    console.log(obj)
                    callback(obj);
                };
                xhr.send();
            }
        });
        
        // 接下来可以通过 cos 实例调用 COS 请求。
        function myUpload(name,fileObject) {
            // 不需要在每个方法里创建一个 COS SDK 实例
            cos.putObject({
                Bucket: Bucket, /* 必须 */
                Region: Region,     /* 存储桶所在地域,必须字段 */
                Key: 'exampleobject',      /* 必须 */
                StorageClass: 'STANDARD',
                Body: fileObject, // 上传文件对象
                onProgress: function(progressData) {
                    console.log(JSON.stringify(progressData));
                }
            }, function(err, data) {
                console.log(err || data);
            });
        }
        // 上传图片
        function uploadImg() {
            var preview = document.querySelector('img');
            var file = document.querySelector('input[type=file]').files[0];
            var reader = new FileReader();

            reader.addEventListener("load", function () {
                let base64Url = reader.result;
                var body = dataURLtoBlob(base64Url);
                myUpload(file.name,body)
            }, false);

            if (file) {
                reader.readAsDataURL(file);
            }
        }
        // dataURL 转 blob
        function dataURLtoBlob(dataurl) {
            var arr = dataurl.split(',');
            var mime = arr[0].match(/:(.*?);/)[1];
            var bstr = atob(arr[1]);
            var n = bstr.length;
            var u8arr = new Uint8Array(n);
            while (n--) {
                u8arr[n] = bstr.charCodeAt(n);
            }
            return new Blob([u8arr], { type: mime });
        };
        </script>
    </body>
</html>

到这为止,前端可以获取到上传图片所需要的Credential了,但是会看到

image.png

所以腾讯云那里还需要设置跨域

腾讯云控制台跨域设置

https://cloud.tencent.com/document/product/436/13318
https://cloud.tencent.com/document/product/436/11488

但我现在是本地开发,跨域设置要有域名,就想着去hosts改改,骗骗他看看行不行

image.png

image.png

在这里改成自己配置的域名

image.png

再试一次,跨域问题解决了,嘻嘻

解决403 forbidden 问题

测试传了一张图片,报了403 Forbidden,Access Denied。

image.png

原来node.js config中的的allowPrefiex相当于是允许上传到哪个文件夹,前端上传的时候要在文件key中写上这个文件夹的名称,要不然就会报403.

node.js文件
image.png

html文件
image.png

配置成这样,就不报403了

登录控制台,可以看到有ticket文件夹

image.png

上传的图片有了

image.png

随便选一个预览,可以哦

image.png

提醒

要看清楚文档,之前我上传图片直接上传dataURL格式,能传上去,但是预览错误,看了文档才发现要转成blob。所以文档一定要看清楚

https://cloud.tencent.com/document/product/436/64960

下载

腾讯云提供了以下几个方法

cos.getObjectUrl

cos.getObject

但在使用之前要在后端的config中加上下载权限
https://cloud.tencent.com/document/product/436/31923

image.png

上一篇 下一篇
Comments
Write a Comment