图片直传阿里云OSS方案

背景

每个OSS的用户都会用到上传服务。Web端常见的上传方法是用户在浏览器或app端上传文件到应用服务器,然后应用服务器再把文件上传到OSS。

和数据直传到OSS相比,以上方法有三个缺点:

  • 上传慢。先上传到应用服务器,再上传到OSS,网络传送比直传到OSS多了一倍。如果直传到OSS,不通过应用服务器,速度将大大提升,而且OSS采用BGP带宽,能保证各地各运营商的速度。
  • 扩展性差。如果后续用户多了,应用服务器会成为瓶颈。
  • 费用高。需要准备多台应用服务器。由于OSS上传流量是免费的,如果数据直传到OSS,不通过应用服务器,那么将能省下几台应用服务器。
目的
Android、IOS端直传实践

Web端直传共有三种方式:

在客户端通过JavaScript代码完成签名,无需过多配置,即可实现直传,非常方便。但是客户端通过JavaScript把AccesssKeyID 和AccessKeySecret写在代码里面有泄露的风险。

web端向服务端请求签名,然后直接上传,不会对服务端产生压力,而且安全可靠。然而有个问题,就是用户上传了多少文件,上传了什么文件,服务端并不能马上知道,如果想实时了解用户上传了什么文件,可以采用第三种方式。

本文将演示如何通过第三种方式完成文件直传到OSS服务器。

demo
前端
window.onload = function () {
    let upload = document.getElementById("upload")
    let link = document.getElementById("link")
    let obj = {}
    // 获取签名地址
    // const url = "http://gt-activity.gtdreamlife.com/api/oss/ossSign"
    axios.get(url).then(res => {
      if (res.data.statusCode === 200) {
          // 下面是签名直传服务返回给客户端的消息body内容的示例,这个body的内容将作为客户端上传文件的重要参数。
        let {
          dirPath,
          key,
          host,
          policy,
          Signature,
          callback,
          OSSAccessKeyId
        } = res.data.result
        obj.host = host
        obj.key = dirPath + key + "${filename}"
        obj.policy = policy
        obj.Signature = Signature
        obj.callback = callback
        obj.OSSAccessKeyId = OSSAccessKeyId
        console.log(obj)
      } else {
        alert(res.data.message)
      }
    })
    document.querySelector("#file").onchange = function (e) {
      let data = e.target.files[0]
      console.log(data)
      let formData = new FormData()
      for (let key in obj) {
        formData.append(key, obj[key])
      }
      // append 文件必须放在最后,不然会报key错误
      formData.append("file", data)
      axios.post(obj.host, formData).then(res => {
        const result = res.data
        document.querySelector("#link").value = result.data
      })
    }
  }
服务端

阿里云OSS配置信息

const config = {
  dirPath: 'oss/file/', // oss 文件夹 不存在会自动创建
  bucket: '${bucket}', // oss应用名
  region: '${region}', // oss节点名
  accessKeyId: '${accessKeyId}', // 申请的osskey
  accessKeySecret: '${accessKeySecret}', // 申请的osssecret
  callbackPath: 'api/oss/ossCallback', // 回调接口
  expAfter: 6000, // 签名失效时间
  maxSize: 1048576000, // 最大文件大小
};

获取签名接口
服务器返回数据Callback API

// 图片签名
  async ossSign() {
    const {
      bucket,
      region,
      expAfter,
      maxSize,
      dirPath,
      accessKeyId,
      accessKeySecret,
      callbackPath,
    } = config;
    const host = `https://${bucket}.${region}.aliyuncs.com`; // 你的oss完整地址
    // 回调接口的主机和端口
    const { callbackHost, callbackPort } = this.app.config.oss;
    const expireTime = new Date().getTime() + expAfter;
    const expiration = new Date(expireTime).toISOString();
    const policyString = JSON.stringify({
      expiration,
      conditions: [
        [ 'content-length-range', 0, maxSize ],
        [ 'starts-with', '$key', dirPath ],
      ],
    });
    const policy = Buffer(policyString).toString('base64');
    // 创建签名
    const Signature = crypto.createHmac('sha1', accessKeySecret).update(policy).digest('base64');
    const callbackBody = {
      // callbackUrl为 上传回调服务器的URL,请将下面的IP和Port配置为您自己的真实信息。
      callbackUrl: `https://${callbackHost}${callbackPort ? `:${callbackPort}` : ''}/${callbackPath}`,
      callbackHost: `${callbackHost}`,
      // 规定返回数据的格式,当前默认返回图片信息、宽度、高度,可获取更多数据。参考上方链接
      callbackBody: '{"filename": ${object},"size": ${size},"width": ${imageInfo.width},"height": ${imageInfo.height}}',
      callbackBodyType: 'application/json',
    };
    const callback = Buffer(JSON.stringify(callbackBody)).toString('base64');
    this.ctx.body = ({
      statusCode: 200,
      message: 'oss签名成功',
      result: {
        Signature, // 对变量policy签名后的字符串。
        policy, // 用户表单上传的策略(Policy),是经过base64编码过的字符串。
        host, // 用户要往哪个域名发送上传请求。
        OSSAccessKeyId: accessKeyId, // 用户请求的accessid。
        key: expireTime, // 唯一标识,添加到上传的文件名中防止重复
        success_action_status: 200,
        dirPath, // 文件上传地址
        callback, // 接口回调
      },
    });
  }

请求回调接口

// 回调
  async ossCallback() {
    const { body = {} } = this.ctx.request;
    // 对应签名接口中定义的返回数据字段
    const { filename, height, width } = body;
    if (filename) {
      this.ctx.body = ({
        status: 200,
        message: '操作成功',
        data: `https://${config.bucket}.${config.region}.aliyuncs.com/${filename}#w=${width}#h=${height}`,
      });
    } else {
      this.ctx.body = {
        status: 500,
        message: '操作失败',
        data: null,
      };
    }
  }
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值