前端圖片直傳阿里OSS實現方法

前端圖片直傳阿里OSS並不是唯一解決方法,也可以放置到後端處理,本文只探討前端圖片直傳阿里OSS實現方式。

 

# 流程:

1、初始化OSS

2、獲取上傳的圖片

3、長傳圖片

 

# 要處理的點:

1:如果用戶上傳的圖片過大,要進行微損壓縮圖片

2:如果蘋果手機旋轉拍照上傳圖片會造成旋轉錯位(兼容性處理)

 

項目採用vant,封裝一個直傳阿里oss的功能,代碼爲:

/**
 * Created by SongPeng on 2019-8-9
 */
import OSS from 'ali-oss'
import { getOssUploadRole } from '@/api/wallet'
import { Toast, Dialog } from 'vant'
import { ossUrl } from '@/config'
import EXIF from 'exif-js'
/**
 * 圖片壓縮 返回base64
 * @param {*base64} base64URL
 */
export function compressImage (base64URL) {
  return new Promise((resolve) => {
    let orientation = ''
    let img = new Image()
    let canvas = document.createElement('canvas')
    let ctx = canvas.getContext('2d')
    img.src = base64URL
    img.onload = () => {
    // 圖片原始尺寸
      let originWidth = img.width
      let originHeight = img.height
      // 最大尺寸限制
      let maxWidth = img.width / 2
      let maxHeight = img.height / 2
      // 目標尺寸
      // 圖片尺寸超過1000x1000的限制
      if (originWidth > maxWidth || originHeight > maxHeight) {
        if (originWidth / originHeight > maxWidth / maxHeight) {
          // 更寬,按照寬度限定尺寸
          img.width = maxWidth
          img.height = Math.round(maxWidth * (originHeight / originWidth))
        } else {
          img.height = maxHeight
          img.width = Math.round(maxHeight * (originWidth / originHeight))
        }
      }
      // canvas對圖片進行縮放
      canvas.width = img.width
      canvas.height = img.height
      // 清除畫布
      ctx.clearRect(0, 0, canvas.width, canvas.height)
      EXIF.getData(img, function () {
        if (navigator.userAgent.match(/iphone/i)) {
          orientation = EXIF.getTag(this, 'Orientation')
          switch (Number(orientation)) {
            case 2:
              ctx.translate(img.width, 0)
              ctx.scale(-1, 1)
              ctx.drawImage(img, 0, 0, img.width, img.height)
              break
            case 3:
              ctx.rotate(180 * Math.PI / 180)
              ctx.drawImage(img, -img.width, -img.height, img.width, img.height)
              break
            case 4:
              ctx.translate(img.width, 0)
              ctx.scale(-1, 1)
              ctx.rotate(180 * Math.PI / 180)
              ctx.drawImage(img, -img.width, -img.height, img.width, img.height)
              break
            case 5:
              ctx.translate(img.width, 0)
              ctx.scale(-1, 1)
              ctx.rotate(90 * Math.PI / 180)
              ctx.drawImage(img, 0, -img.width, img.height, img.width)
              break
            case 6:
              canvas.width = img.height
              canvas.height = img.width
              ctx.rotate(90 * Math.PI / 180)
              ctx.drawImage(img, 0, 0, img.width, -img.height)
              break
            case 7:
              ctx.translate(img.width, 0)
              ctx.scale(-1, 1)
              ctx.rotate(270 * Math.PI / 180)
              ctx.drawImage(img, -img.height, 0, img.height, img.width)
              break
            case 8:
              ctx.rotate(270 * Math.PI / 180)
              ctx.drawImage(img, -img.height, 0, img.height, img.width)
              break
            default:
              ctx.drawImage(img, 0, 0, img.width, img.height)
              break
          }
        } else {
          ctx.drawImage(img, 0, 0, img.width, img.height)
        }
      })
      let base64 = canvas.toDataURL('image/jpeg', 0.7)
      resolve(base64)
    }
  })
}

/**
* 將以base64的圖片url數據轉換爲Blob
* @param urlData圖片base64數據
* @name convertBase64UrlToBlob
*/

export function convertBase64UrlToBlob (urlData) {
  let bytes = window.atob(urlData.split(',')[1]) // 去掉url的頭,並轉換爲byte
  // 處理異常,將ascii碼小於0的轉換爲大於0
  let ab = new ArrayBuffer(bytes.length)
  let ia = new Uint8Array(ab)
  for (let i = 0; i < bytes.length; i++) {
    ia[i] = bytes.charCodeAt(i)
  }
  return new Blob([ab], {
    type: 'image/jpg'
  })
}
/**
  * 將以base64的圖片url數據轉換爲file
  * @param dataurl 圖片base64數據
  * @param filename 圖片名稱
  * @name dataURLtoFile
  */

export function dataURLtoFile (dataurl, filename) { // 將base64轉換爲文件
  let arr = dataurl.split(',')
  let mime = arr[0].match(/:(.*?);/)[1]
  let bstr = atob(arr[1])
  let n = bstr.length
  let u8arr = new Uint8Array(n)
  while (n--) {
    u8arr[n] = bstr.charCodeAt(n)
  }
  return new File([u8arr], filename, {
    type: mime
  })
}
const aliOss = {
  client: null,
  fileName: '',
  file: null,
  loading: null,
  async setClient () {
    if (this.client === null) {
      const res = await getOssUploadRole()
      if (res.code === 0) {
        let result = res.data
        this.client = new OSS({
          accessKeyId: result.accessKeyId,
          accessKeySecret: result.accessKeySecret,
          stsToken: result.stsToken,
          endpoint: result.endpoint,
          bucket: result.bucket
        })
      }
    }
  },
  uploadImg (fileName, file) {
    Toast.loading({
      mask: true,
      duration: 0,
      message: '上傳中...'
    })
    return new Promise((resolve, reject) => {
      this.fileName = fileName
      this.file = file
      if (file.file.size / 1024 / 1024 > 0.8) {
        compressImage(this.file.content)
          .then(base64 => {
            this.file.file = convertBase64UrlToBlob(base64)
            this.readyLoad(this.fileName, this.file.file)
              .then(url => {
                resolve(url)
              })
          })
      } else {
        this.readyLoad(this.fileName, this.file.file)
          .then(url => {
            resolve(url)
          })
      }
    })
  },
  readyLoad (fileName, file) {
    return new Promise((resolve, reject) => {
      this.client.multipartUpload(fileName, file)
        .then(function (result) {
          Toast.clear()
          Toast({
            type: 'success',
            message: '上傳成功',
            duration: 1500
          })
          resolve(`${ossUrl}/${fileName}`)
        })
        .catch(function (err) {
          Toast.clear()
          if (err.indexOf('timeout') !== -1) {
            Dialog({
              title: '提示',
              message: '上傳超時'
            })
          } else {
            Dialog({
              title: '提示',
              message: '上傳失敗'
            })
          }
        })
    })
  }
}

export default aliOss

具體使用方法:

import aliOss from '@/utils/uploadImg'  // 第一步引入
this.initOss()  // 初始化OSS 客戶端
onRead (file, options) {
      const data = file.file.name.split('.')
      let imgType = data[data.length - 1]
      let fileName = `img/${options.name}-${this.$store.state.userInfo.id}-${Date.parse(new Date())}.${imgType}`
      aliOss.uploadImg(fileName, file)
        .then(url => {
          // 上傳過oss後再同步圖片地址到服務器
          this.$store.dispatch('brand/setBrandLogo', url)
            .then(res => {
              if (res.code === 0) {
                this.$Toast.success(res.message)
              }
            })
        })
    }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章