用简单的代码,将小程序文件直传到腾讯云COS实践

简介

本文介绍如何不依赖 SDK,用简单的代码,在小程序直传文件到腾讯云COS的存储桶。

注意:

本文档内容基于 XML 版本的 API。

前期条件

  1. 登录 对象存储控制台 ,创建存储桶,设置 BucketName(存储桶名称) 和 Region(地域名称),详情请参见 创建存储桶 文档。
  2. 登录 访问管理控制台,进入 API 密钥管理页面,获取您的项目 SecretId 和 SecretKey。

注意:

目前腾讯云有COS特惠活动,新人1元起

实践步骤

1. 配置小程序域名白名单

小程序里请求腾讯云COS需要登录到微信公众平台,在“开发”->“开发设置”中,配置域名白名单。SDK 用到了两个接口:wx.uploadFile 和 wx.request。

  • cos.postObject 使用 wx.uploadFile 发送请求。
  • 其他方法使用 wx.request 发送请求。

两者都需要在对应白名单里,配置 COS 域名。白名单里配置的域名格式有两种:

  • 如果只用到一个存储桶,可以配置 Bucket 域名作为白名单域名,例如examplebucket-1250000000.cos.ap-guangzhou.myqcloud.com
  • 如果用到多个存储桶,可以选择后缀式请求 COS,把 bucket 放在 pathname 里请求,这种方式需要配置地域域名作为白名单,例如cos.ap-guangzhou.myqcloud.com。具体可参考下文代码中的注释。

2. 获取临时密钥和计算签名

出于安全考虑,签名使用临时密钥,服务端搭建临时密钥服务,请参见 PHP 示例Nodejs 示例
如有其他语言或自行实现可参考以下流程:
(1)向服务端获取临时密钥,服务端首先使用固定密钥 SecretId、SecretKey 向 STS 服务获取临时密钥,得到临时密钥 tmpSecretId、tmpSecretKey、sessionToken,详情请参见 临时密钥生成及使用指引 或 cos-sts-sdk 。

注意:

根据使用的请求是put还是post,STS 的 policy action 要加上允许 "name/cos:PutObject"或"name/cos:PostObject"。

(2)前端通过 tmpSecretId、tmpSecretKey,以及 method、pathname 计算签名,可参考下文使用 cos-auth.js 来计算签名,如果业务需要也可以放在后端计算签名。
(3)将计算得到的签名和 sessionToken,分别放到

  • post请求的formData 的 Signature 和 x-cos-security-token 字段里,向 COS API 发出上传请求。
  • put请求的headers 的 Signature 和 x-cos-security-token 字段里,向 COS API 发出上传请求。

注意:

正式部署时服务端请加一层您的网站本身的权限检验。

3. 后缀式请求

COS API 一般的请求格式都类似POST http://examplebucket-1250000000.cos.ap-beijing.myqcloud.com/,请求的域名是存储桶域名。这样如果在小程序里用到多个存储桶,则需要配置这个存储桶域名作为白名单域名。解决方法如下:

COS 提供了后缀式请求格式POST http://cos.ap-beijing.myqcloud.com/examplebucket-1250000000/,请求的域名是地域域名,存储桶名称放在请求的路径里。在小程序里用到同一个地域多个存储桶,只需要配置一个域名cos.ap-beijing.myqcloud.com作为白名单域名。

后缀式请求格式需要注意,签名时使用的路径要用以存储桶名称作为前缀的路径,例如/examplebucket-1250000000/

4. 直传示例代码

以下代码同时举例了 PUT Object 接口(推荐使用)和POST Object 接口,操作指引如下:

var CosAuth = require('./cos-auth'); // 这里引用了 cos-auth.js,下载地址为 https://unpkg.com/cos-js-sdk-v5/demo/common/cos-auth.min.js 

var Bucket = 'examplebucket-1250000000';
var Region = 'ap-shanghai';
var ForcePathStyle = false; // 是否使用后缀式,涉及签名计算和域名白名单配置,后缀式说明看上文

var uploadFile = function () {

    // 请求用到的参数
    var prefix = 'https://' + Bucket + '.cos.' + Region + '.myqcloud.com/';
    if (ForcePathStyle) {
        // 后缀式请求在签名时域名使用地域域名,而不是存储桶域名,具体说明见本文上述“3.后缀式请求”
        prefix = 'https://cos.' + Region + '.myqcloud.com/' + Bucket + '/'; 
    }

    // 对更多字符编码的 url encode 格式
    var camSafeUrlEncode = function (str) {
        return encodeURIComponent(str)
            .replace(/!/g, '%21')
            .replace(/'/g, '%27')
            .replace(/\(/g, '%28')
            .replace(/\)/g, '%29')
            .replace(/\*/g, '%2A');
    };

    // 获取临时密钥
    var stsCache;
    var getCredentials = function (callback) {
        if (stsCache && Date.now() / 1000 + 30 < stsCache.expiredTime) {
            callback(data.credentials);
            return;
        }
        wx.request({
            method: 'GET',
            url: 'https://example.com/sts.php', // 服务端签名,参考上文说的获取临时密钥
            dataType: 'json',
            success: function (result) {
                var data = result.data;
                var credentials = data.credentials;
                if (credentials) {
                    stsCache = data
                } else {
                    wx.showModal({title: '临时密钥获取失败', content: JSON.stringify(data), showCancel: false});
                }
                callback(stsCache && stsCache.credentials);
            },
            error: function (err) {
                wx.showModal({title: '临时密钥获取失败', content: JSON.stringify(err), showCancel: false});
            }
        });
    };

    // 计算签名
    var getAuthorization = function (options, callback) {
        getCredentials(function (credentials) {
            callback({
                XCosSecurityToken: credentials.sessionToken,
                Authorization: CosAuth({
                    SecretId: credentials.tmpSecretId,
                    SecretKey: credentials.tmpSecretKey,
                    Method: options.Method,
                    Pathname: options.Pathname,
                })
            });
        });
    };

    // post上传文件
    var postFile = function (filePath) {
        var Key = filePath.substr(filePath.lastIndexOf('/') + 1); // 这里指定上传的文件名
        var signPathname = '/'; // PostObject 接口 Key 是放在 Body 传输,所以请求路径和签名路径是 /
        if (ForcePathStyle) {
              // 后缀式请求在签名时用的路径,要包含存储桶名称,具体说明见本文上述“3.后缀式请求”
            signPathname = '/' + Bucket + '/';  
        }
        getAuthorization({Method: 'POST', Pathname: signPathname}, function (AuthData) {
            var requestTask = wx.uploadFile({
                url: prefix,
                name: 'file',
                filePath: filePath,
                formData: {
                    'key': Key,
                    'success_action_status': 200,
                    'Signature': AuthData.Authorization,
                    'x-cos-security-token': AuthData.XCosSecurityToken,
                    'Content-Type': '',
                },
                success: function (res) {
                    var url = prefix + camSafeUrlEncode(Key).replace(/%2F/g, '/');
                    console.log(res.statusCode);
                    console.log(url);
                    if (/^2\d\d$/.test('' + res.statusCode)) {
                        wx.showModal({title: '上传成功', content: url, showCancel: false});
                    } else {
                        wx.showModal({title: '上传失败', content: JSON.stringify(res), showCancel: false});
                    }
                },
                fail: function (res) {
                    wx.showModal({title: '上传失败', content: JSON.stringify(res), showCancel: false});
                }
            });
            requestTask.onProgressUpdate(function (res) {
                console.log('进度:', res);
            });
        });
    };

    // put上传文件
    var putFile = function (filePath) {
        var Key = filePath.substr(filePath.lastIndexOf('/') + 1); // 这里指定上传的文件名
        var signPathname = '/' + Key; // PutObject 接口 Key 是放在 url 传输,所以请求路径和签名路径是 /Key
        if (ForcePathStyle) {
              // 后缀式请求在签名时用的路径,要包含存储桶名称,具体说明见本文上述“3.后缀式请求”
            signPathname = '/' + Bucket + '/' + Key;  
        }
        getAuthorization({Method: 'PUT', Pathname: signPathname}, function (AuthData) {
          // put请求需要从文件临时路径读取出文件内容
          var wxfs = wx.getFileSystemManager();
          wxfs.readFile({
            filePath: filePath,
            success: function (fileRes) {
              var requestTask = wx.request({
                url: prefix + signPathname.substr(signPathname.lastIndexOf('/') + 1),
                method: 'PUT',
                header: {
                  'Authorization': AuthData.Authorization,
                  'x-cos-security-token': AuthData.XCosSecurityToken,
                },
                data: fileRes.data,
                success: function success(res) {
                  var url = prefix + camSafeUrlEncode(Key).replace(/%2F/g, '/');
                  if (res.statusCode === 200) {
                      wx.showModal({title: '上传成功', content: url, showCancel: false});
                  } else {
                      wx.showModal({title: '上传失败', content: JSON.stringify(res), showCancel: false});
                  }
                  console.log(res.statusCode);
                  console.log(url);
                },
                fail: function fail(res) {
                  wx.showModal({title: '上传失败', content: JSON.stringify(res), showCancel: false});
                },
              });
              requestTask.onProgressUpdate(function (res) {
                  console.log('正在进度:', res);
              });
            },
          });
        });
    };

    // 选择文件
    wx.chooseImage({
        count: 1, // 默认9
        sizeType: ['original'], // 可以指定是原图还是压缩图,这里默认用原图
        sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
        success: function (res) {
            putFile(res.tempFiles[0].path); // put请求上传,推荐使用
            // postFile(res.tempFiles[0].path); // post请求上传
        }
    })
};
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值