圖片直傳阿里雲OSS方案

背景

每一個OSS的用戶都會用到上傳服務。Web端常見的上傳方法是用戶在瀏覽器或app端上傳文件到應用服務器,而後應用服務器再把文件上傳到OSS。html

和數據直傳到OSS相比,以上方法有三個缺點:前端

  • 上傳慢。先上傳到應用服務器,再上傳到OSS,網絡傳送比直傳到OSS多了一倍。若是直傳到OSS,不經過應用服務器,速度將大大提高,並且OSS採用BGP帶寬,能保證各地各運營商的速度。
  • 擴展性差。若是後續用戶多了,應用服務器會成爲瓶頸。
  • 費用高。須要準備多臺應用服務器。因爲OSS上傳流量是免費的,若是數據直傳到OSS,不經過應用服務器,那麼將能省下幾臺應用服務器。

目的

Android、IOS端直傳實踐

Web端直傳共有三種方式:ios

在客戶端經過JavaScript代碼完成簽名,無需過多配置,便可實現直傳,很是方便。可是客戶端經過JavaScript把AccesssKeyID 和AccessKeySecret寫在代碼裏面有泄露的風險。web

web端向服務端請求籤名,而後直接上傳,不會對服務端產生壓力,並且安全可靠。然而有個問題,就是用戶上傳了多少文件,上傳了什麼文件,服務端並不能立刻知道,若是想實時瞭解用戶上傳了什麼文件,能夠採用第三種方式。json

本文將演示如何經過第三種方式完成文件直傳到OSS服務器。axios

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配置信息api

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,
      };
    }
  }
相關文章
相關標籤/搜索