腾讯云对象存储COS的使用教程,node获取签名,js上传

腾讯云对象存储的使用

这里忽略创建存储桶之类的操作。
说明:本示例使用nuxt服务端渲染 + koa框架

万事第一步:npm install ~~~
npm i qcloud-cos-sts --save

node sdk

注意:qcloud-cos-sts 只能在服务端中使用。
第二步:设置授权策略

// 示例 创建一个oss文件夹在controllers下面,项目结构自行更改。
// ../controllers/oss/index.js

let STS = require('qcloud-cos-sts'); 

import jwt from '../../../jwt.js'; // 封装的jwt方法
import { response as R } from '../../controllers/unifyResponse'; // 这里对数据封装了统一返回格式,后面会附上封装源码。
// ······

/**
 * 授权策略
 * @param {String} LongBucketName 桶名
 * @param {String} Region 桶区域
 */
let policy = function(LongBucketName, Region) {
  // let LongBucketName = 'public-***********';
  // let Region = 'ap-beijing';
  let ShortBucketName = LongBucketName.substr(0, LongBucketName.indexOf('-'));
  let AppId = LongBucketName.substr(LongBucketName.indexOf('-') + 1);
  return {
    version: '2.0' /* 策略语法版本,默认为2.0 */,
    statement: [
      {
        action: [
          // '*'
          // 所有 action 请看文档 https://cloud.tencent.com/document/product/436/31923
          // 简单上传
          'name/cos:PutObject',
          'name/cos:PostObject',
          // 分片上传
          'name/cos:InitiateMultipartUpload',
          'name/cos:ListMultipartUploads',
          'name/cos:ListParts',
          'name/cos:UploadPart',
          'name/cos:CompleteMultipartUpload',
          'name/cos:uploadFiles'
        ] /* 此处是指 COS API,根据需求指定一个或者一序列操作的组合或所有操作(*) */,
        effect: 'allow' /* 有 allow (允许)和 deny (显式拒绝)两种情况 */,
        principal: { qcs: ['*'] } /* 委托人 授权子账户权限 */,
        resource: ['qcs::cos:' + Region + ':uid/' + AppId + ':prefix//' + AppId + '/' + ShortBucketName + '/*'] /* 授权操作的具体数据,可以是任意资源、指定路径前缀的资源、指定绝对路径的资源或它们的组合 */
      }
    ]
  };
};

第三步:接下来获取临时签名

/**
 * 鉴权获取oss临时密钥接口
 * @param {*} ctx nuxt上下文对象
 * @param {*} next 执行下一步
 * @returns 返回临时密钥
 */
let getOssKey = (ctx, next) => {
  return new Promise(async (resolve, reject) => {
    try {
      // await jwt.verifyToken(ctx, next); /* 这里是对于用户访问进行鉴权,可以根据实际项目需求修改。 */
      STS.getCredential(
        {
          secretId: 'AKID3zjGZgCxCOQCk8IBxWwAC0ZigU4tpzBQ',
          secretKey: '2rRmsxlSxDGZdWSGtJrBqGYeaNxiSUIr',
          policy: policy(ctx.request.body.Bucket, ctx.request.body.Region),
          durationSeconds: 7200
        },
        async (err, credential) => {
          console.log(err || credential) // 这里拿到错误或者临时签名信息。
          /*  R.send()为封装的统一返回方法,请自行删减。 */
          return resolve((ctx.body = await R.send(200, err || credential)));
        }
      );
    } catch (error) {
      //   console.log('getOssKey:', error);
      return resolve((ctx.body = error));
    }
  });
};

export default {
  getOssKey /* 获取临时密钥 */
};

我这是暴露了一个接口供前端调用

// 多余说明:
const Router = require('koa-router');
const router = new Router({ prefix: '/koaApi/manage' }); // prefix 为router前缀,根据具体需求增删改~。
import oss from "../controllers/oss/index";
router.post('/getOssKey', oss.getOssKey);

ok 服务端这时候拿到临时COS的签名,下面则是客户端的骚操作了。

前端请求格式多余赘述:

  1. axios API 封装格式。( 后面附上 server+client 统一axios请求拦截器 )
export const getOssKey = async params => {
  return await global.axios.$post(`/manage/getOssKey`, params);
};
// 调用api使用 getOssKey({ Bucket: 'public-********', Region: 'ap-beijing' })
  1. 直接使用 axios 请求
axios.$post(`/manage/getOssKey`, { Bucket: 'public-********', Region: 'ap-beijing' });

客户端COS上传

第一步:下载cos-js-sdk-v5,很抱歉万年不变的定律npm install在这里行不通,使用 npm包下载的sdk运行貌似有问题,各种报错。( 或者你们可以尝试一下 😊)
我们通过gayhub下载~ 点击进入查看源码 👉cos-js-sdk-v5,将源码复制下载放在static或者plugins文件夹即可。

第二步:引入 plugins
nuxt.config.js中配置plugins
注意:cos-js-sdk-v5 只能在客户端中使用。

// nuxt.config.js
plugins:[{ src: '~/static/cos-js-sdk.js', mode: 'client' }]

第三步:客户端COS上传jssdk模块封装

import { getOssKey } from '../../assets/utils/api.js';  // 封装的api接口
import { Toast } from 'vant'; // 这里使用了vant的loading,可自行增删。
import meth from './meth.js'; // 封装的公用methods模块,下面附上meth模块中使用到的方法。如果只需要测试上传demo 那么只需要拿取其中的random_string+randomSign方法即可。
// ...
// 进入正题
export default new (class {
  constructor() {}
  _cos(obj = { Bucket: 'public-********', Region: 'ap-beijing' }) {
    return new COS({
      getAuthorization: function(options, callback) {
        getOssKey({ Bucket: obj.Bucket, Region: obj.Region }).then(config => {
          callback({
            TmpSecretId: config.data.credentials.tmpSecretId,
            TmpSecretKey: config.data.credentials.tmpSecretKey,
            XCosSecurityToken: config.data.credentials.sessionToken,
            ExpiredTime: config.data.expiredTime, // SDK 在 ExpiredTime 时间前,不会再次调用 getAuthorization
            // ScopeLimit: true,  // 细粒度控制权限设为 true,会限制密钥只在相同请求时重复使用
            FileParallelLimit: 3, // 控制文件上传并发数
            ChunkParallelLimit: 8 // 控制单个文件下分片上传并发数,在同园区上传可以设置较大的并发数
            // ChunkSize: 1024 * 1024 * 8,  // 控制分片大小,单位 B,在同园区上传可以设置较大的分片大小
          });
        });
      }
    });
  }
  /**
   * 文件直传
   * @param {Object} obj Bucket、Region、Body 详情查看cos文档
   * @returns err || data
   */
  putObject(obj) {
    return new Promise((resolve, reject) => {
      this._cos(obj).putObject(
        {
          Bucket: obj.Bucket,
          Region: obj.Region,
          Key: `${meth.randomSign(3)}.${obj.Body[0].name.split('.')[1]}`,
          Body: obj.Body[0],
          onTaskReady: function(taskId) {
            /* 执行队列taskId */
            console.log('taskId:', taskId);
          },
          onProgress: info => {
            var percent = parseInt(info.percent * 10000) / 100;
            var speed = parseInt((info.speed / 1024 / 1024) * 100) / 100;
            console.log('进度:' + percent + '%; 速度:' + speed + 'Mb/s;');
            this.loading('进度:' + percent + '%');
          }
        },
        (err, data) => {
          Toast.clear();
          return err ? Toast.fail('上传失败') && reject(err) : Toast.success('上传成功') && resolve(data);
        }
      );
    });
  }
  /**
   * 文件分块上传
   * @param {Object} obj Bucket、Region、Body 详情查看cos文档
   * @returns err || data
   */
  sliceUploadFile(obj) {
    return new Promise((resolve, reject) => {
      this._cos(obj).sliceUploadFile(
        {
          Bucket: obj.Bucket,
          Region: obj.Region,
          Key: `${meth.randomSign(3)}.${obj.Body[0].name.split('.')[1]}`,
          Body: obj.Body[0],
          onTaskReady: function(taskId) {
            /* 执行队列taskId */
            console.log('taskId:', taskId);
          },
          onProgress: progressData => {
            /* 非必须 */
            var percent = parseInt(progressData.percent * 10000) / 100;
            var speed = parseInt((progressData.speed / 1024 / 1024) * 100) / 100;
            console.log('进度:' + percent + '%; 速度:' + speed + 'Mb/s;');
            this.loading('进度:' + parseInt(percent) + '%');
          }
        },
        (err, data) => {
          Toast.clear();
          return err ? Toast.fail('上传失败') && reject(err) : Toast.success('上传成功') && resolve(data);
        }
      );
    });
  }
  /**
   * 批量上传
   * @param {Object} obj Bucket、Region、Body 详情查看cos文档
   * @returns err || 批量上传filesData
   */
  MultiFiles(obj) {
    return new Promise(async (resolve, reject) => {
      let files = await [...obj.Body].map(file => ({ Bucket: obj.Bucket, Region: obj.Region, Key: `${meth.randomSign(3)}.${file.name.split('.')[1]}`, Body: file }));
      await this._cos({ Bucket: obj.Bucket, Region: obj.Region }).uploadFiles(
        {
          files: files,
          SliceSize: 1024 * 1024,
          onTaskReady: function(taskId) {
            /* 执行队列taskId */
            // console.log("taskId:",taskId);
          },
          onProgress: info => {
            var percent = parseInt(info.percent * 10000) / 100;
            var speed = parseInt((info.speed / 1024 / 1024) * 100) / 100;
            console.log('进度:' + percent + '%; 速度:' + speed + 'Mb/s;');
            this.loading('进度:' + percent + '%');
          },
          onFileFinish: (err, data, options) => {
            console.log(options.Key + '上传' + (err ? '失败' : '完成'));
          }
        },
        (err, data) => {
          Toast.clear();
          return err ? Toast.fail('上传失败') && reject(err) : Toast.success('上传成功') && resolve(data);
        }
      );
    });
  }
  loading(text) {
    try {
      Toast.loading({
        duration: 0, // 持续展示 toast
        forbidClick: true,
        message: text
      });
    } catch (error) {
      console.log(error);
    }
  }
})();

client 客户端COS API调用

putObject 文件直传

await tools.putObject({
    Bucket: 'public-********', /* 必须 */
    Region: 'ap-beijing',    /* 必须 */
    Body: e.target.files
})

sliceUploadFile 分块上传

await tools.sliceUploadFile({
    Bucket: 'public-********', /* 必须 */
    Region: 'ap-beijing',    /* 必须 */
    Body: e.target.files
})

MultiFiles 批量上传 需要设置 inputmultiple

await tools.MultiFiles({
    Bucket: 'public-********', /* 必须 */
    Region: 'ap-beijing',    /* 必须 */
    Body: e.target.files
})

这里便可以实现客户端使用cos-js-sdk进行上传文件了
示例图:
在这里插入图片描述
在这里插入图片描述

下面附上公用方法

meth.js
说明:上传的Key使用了meth中的随机字符串方法randomSign

 export default new (class {
  constructor() {}
  /**
   * 设置cookie
   * @param {String} name cookie的名称
   * @param {String} value cookie的值
   * @param {Number} expiredays cookie的过期时间
   */
  setCookie(name, value, expiredays) {
    var exdate = new Date();
    exdate.setDate(exdate.getDate() + expiredays);
    document.cookie = name + '=' + encodeURI(value) + (expiredays == null ? '' : ';expires=' + exdate.toGMTString());
  }
  /**
   * 获取cookie
   * @param {String} name cookie的名称
   */
  getCookie(name) {
    var arr,
      reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)');
    if ((arr = document.cookie.match(reg))) {
      return arr[2];
    } else {
      return null;
    }
  }
  /**
   * 删除cookie
   * @param {String} name cookie的名称
   */
  delCookie(name) {
    var exp = new Date();
    exp.setTime(exp.getTime() - 1);
    var cval = this.getCookie(name);
    if (cval != null) {
      document.cookie = name + '=' + cval + ';expires=' + exp.toGMTString();
    }
  }
  /**
   * 表单上传转formdata编码
   * @param {Object} data
   * @returns formdata
   */
  fromData(data) {
    if (process.client) {
      let formData = new FormData();
      Object.entries(data).map(([k, v]) => {
        formData.append(k, v);
      });
      return formData;
    }
  }
  /**
   * 生成随机数
   * @param {Num} len 需要生成的随机数长度
   */
  random_string(len) {
    //获取随机名
    len = len || 32;
    let chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz';
    let maxPos = chars.length;
    let pwd = '';
    for (let i = 0; i < len; i++) {
      pwd += chars.charAt(Math.floor(Math.random() * maxPos));
    }
    return pwd;
  }
  /**
   * 生成随机req_msg_id === 随机数+时间戳
   * @param {Num} len 需要生成的随机数长度
   */
  randomSign(len) {
    return `${this.random_string(len)}-${parseInt(Math.floor(Math.random() * Date.now()) / 1000)}`;
  }
  //建立一个可存取到该file的url
  getObjectURL(file) {
    var url = null;
    if (window.createObjectURL != undefined) {
      // basic
      url = window.createObjectURL(file);
    } else if (window.URL != undefined) {
      // mozilla(firefox)
      url = window.URL.createObjectURL(file);
    } else if (window.webkitURL != undefined) {
      // webkit or chrome
      url = window.webkitURL.createObjectURL(file);
    }
    return url;
  }
})();

jwt封装方法

jwt.js

/*
 * @Author: yaodongyi
 * @Date: 2019-09-29 16:01:09
 * @Description:jsonwebtoken
 */
// jwt生成token
const jwt = require('jsonwebtoken');
import { response as R } from './db/controllers/unifyResponse'; // 封装的统一响应状态结果

const serect = 'serect';
/**
 * 生成token
 * @param {Object} userinfo 用户信息
 * @returns token
 */
let addToken = async userinfo => {
  //创建token并导出
  const token = jwt.sign(
    {
      name: userinfo.name,
      id: userinfo.id
    },
    serect,
    { expiresIn: '7d' }
  );
  return token;
};

/**
 * 验证token是否过期
 * @param {String} tokens
 * @returns false未过期,true过期
 */
let verifyToken = (ctx, next) => {
  return new Promise((resolve, rejects) => {
    let token = ctx.request.header.token || ctx.request.header.cookie;
    if (!token) {
      return rejects(R.send(0, {}, '暂未登录,请先进行登录'));
    }
    try {
      resolve(Object.assign(200, jwt.verify(token, serect)));
    } catch (err) {
      return rejects(R.send(0, err, '登录失效,请重新登录'));
    }
  });
};

/**
 * 解密token
 * @param {Object} tokens
 * @returns 解密后信息
 */
let decodeToken = tokens => {
  if (tokens) {
    let decoded = jwt.decode(tokens, serect);
    return decoded;
  }
};

/**
 * @description demo
 * @example
 * try {
 *   console.log(jwt.verifyToken(jwt.addToken({ name: 'ydy', id: 1 })));
 * } catch (error) {
 *   console.log(error);
 * }
 */

module.exports = { addToken: addToken, decodeToken: decodeToken, verifyToken: verifyToken };

统一返回数据格式封装

unifyResponse.js

/*
 * @Author: yaodongyi
 * @Date: 2019-09-30 14:46:07
 * @Description:
 */
export const response = new (class {
  constructor() {
    /**
     * 返回结构体
     */
    this.response = {
      result: {
        code: new Number(), // 200:success, 0:error, default 0:Invalid
        msg: new String() // description
      },
      data: new Object() // 响应body体
    };
  }
  /**
   * 设置统一返回结构
   * @param {*} code 返回状态码
   * @param {*} data 返回的数据源
   * @param {*} description 返回的msg
   * @returns {Object} { result: { code: Number, msg: String }, data: Object }
   */
  send(code, data, description) {
    switch (code) {
      case 200:
        return Object.assign({}, this.response, {
          data: data,
          result: {
            code: 200,
            msg: description || 'Success'
          }
        });
      case 0:
        return Object.assign({}, this.response, {
          result: {
            code: 0,
            msg: description || 'Error'
          }
        });
      default:
        return Object.assign({}, this.response, {
          result: {
            code: 0,
            msg: description || 'Invalid'
          }
        });
    }
  }
})();

封装统一axios请求拦截器

plugins/http.js

/*
 * @Author: yaodongyi
 * @Date: 2019-07-26 16:12:05
 * @Description:axios请求相应拦截器
 */
// import qs from 'qs';
// import { Notification } from 'element-ui';
// import { res_decode } from '../assets/utils/encode';
// import meth from '../assets/utils/meth';
// 如果后端服务器地址和前端服务器域名不同 or 生产环境/开发环境 域名不同,则对应设置域名。
// const baseURL = process.env.NODE_ENV === 'development' ? '/koaApi' : 'http://www.baidu.com';

let getCookie = (cookie = '', name) => (cookie.match(new RegExp('(^| )' + name + '=([^;]*)(;|$)')) ? cookie.match(new RegExp('(^| )' + name + '=([^;]*)(;|$)'))[2] : null);

export default function(ctx, inject) {
  // ctx.$axios.defaults.timeout = 6000;
  ctx.$axios.interceptors.request.use(request => {
    // console.log(request.headers.common);
    request.headers = Object.assign(
      {
        // 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
        'Content-Type': request.headers.ContentType ? request.headers.ContentType : 'application/json; charset=UTF-8', //json
        Accept: 'application/json'
      },
      { ...(process.server && { Authorization: getCookie(request.headers.common.cookie, 'token') || '' }), ...(process.client && { token: sessionStorage.getItem('token') || '' }) }
    );

    if (process.client) {
      request.baseURL = '/koaApi';
      console.log(`%c 发送 ${request.baseURL}${request.url} `, 'background:#00CC6E;color:#ffffff', request);
    }
    return request;
  });

  ctx.$axios.onRequestError(config => {
    console.error('请求错误' + config);
  });

  ctx.$axios.interceptors.response.use(
    response => {
      // console.log(process.server, 'response.data', response);
      // response.data.data.encodeType && res_decode(response); // 这里是rsa解密,根据具体需求进行增删。
      if (process.server) return Promise.resolve(response);
      if (response.status === 200) {
        if (response.data.result.code === 200) {
          console.log(`%c 接收 ${response.config.url} `, 'background:#1E1E1E;color:#bada55', response);
        } else {
          // Notification({
          //   message: response.data.result.msg,
          //   type: 'warning'
          // });
          console.error(`接收 ${response.config.url} `, response);
        }
      } else {
        console.error(`接收 ${response.config.url} `, response);
      }
      return Promise.resolve(response);
    },
    error => {
      return Promise.reject(error);
    }
  );

  ctx.$axios.onError(config => {
    console.error('错误' + config);
  });

  /* 坑点,注入全局做法。 */
  global = Object.assign(global, { axios: ctx.$axios });
}

nuxt.config.js中引入plugins

plugins:['~/plugins/http']	

封装统一管理 API 接口

export const getOssKey = async params => {
  return await global.axios.$post(`/manage/getOssKey`, params);
};

使用:

getOssKey({ Bucket: obj.Bucket, Region: obj.Region }).then(res=>{
	// ... 
}).catch(err=>{
	// ...
})
// or
async func(){
	await getOssKey({ Bucket: obj.Bucket, Region: obj.Region })
}
  • 6
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值