vue+elementui+web上傳圖片壓縮

注意事項(xiàng):未解決PNG帶通道透明圖片壓縮后黑屏的問題,網(wǎng)上有評論說繪制背景色白色

context.fillStyle = '#fff'

個(gè)人覺得意義不大,因?yàn)橹灰亲鲈O(shè)計(jì)的都明白透明通道和白色背景不是同一個(gè)概念。
為啥不支持PNG?
點(diǎn)擊此處查看原因

原因總結(jié):會(huì)用到toDataURL('image/jpeg',qualitys)的一個(gè)方法

在指定圖片格式為 image/jpeg 或 image/webp的情況下,可以從 0 到 1 的區(qū)間內(nèi)選擇圖片的質(zhì)量。如果超出取值范圍,將會(huì)使用默認(rèn)值 0.92,其他參數(shù)會(huì)被忽略。
文件工具:compress.js

export default {
  compressImg: function(file, quality) {
    var qualitys = 0.52
    // console.log(parseInt((file.size / 1024).toFixed(2)))
    if (parseInt((file.size / 1024).toFixed(2)) < 1024) {
      qualitys = 0.85
    }
    if (5 * 1024 < parseInt((file.size / 1024).toFixed(2))) {
      qualitys = 0.92
    }
    if (quality) {
      qualitys = quality
    }
    if (file[0]) {
      return Promise.all(
        Array.from(file).map(e => this.compressImg(e, qualitys))
      ) // 如果是 file 數(shù)組返回 Promise 數(shù)組
    } else {
      return new Promise(resolve => {
        // console.log(file)
        if ((file.size / 1024).toFixed(2) < 300) {
          resolve({
            file: file
          })
        } else {
          const reader = new FileReader() // 創(chuàng)建 FileReader
          reader.onload = ({ target: { result: src } }) => {
            const image = new Image() // 創(chuàng)建 img 元素
            image.onload = async () => {
              const canvas = document.createElement('canvas') // 創(chuàng)建 canvas 元素
              const context = canvas.getContext('2d')
              var targetWidth = image.width
              var targetHeight = image.height
              var originWidth = image.width
              var originHeight = image.height
              if (
                1 * 1024 <= parseInt((file.size / 1024).toFixed(2)) &&
                parseInt((file.size / 1024).toFixed(2)) <= 10 * 1024
              ) {
                var maxWidth = 1600
                var maxHeight = 1600
                targetWidth = originWidth
                targetHeight = originHeight
                // 圖片尺寸超過的限制
                if (originWidth > maxWidth || originHeight > maxHeight) {
                  if (originWidth / originHeight > maxWidth / maxHeight) {
                    // 更寬,按照寬度限定尺寸
                    targetWidth = maxWidth
                    targetHeight = Math.round(
                      maxWidth * (originHeight / originWidth)
                    )
                  } else {
                    targetHeight = maxHeight
                    targetWidth = Math.round(
                      maxHeight * (originWidth / originHeight)
                    )
                  }
                }
              }
              if (
                10 * 1024 <= parseInt((file.size / 1024).toFixed(2)) &&
                parseInt((file.size / 1024).toFixed(2)) <= 20 * 1024
              ) {
                maxWidth = 1400
                maxHeight = 1400
                targetWidth = originWidth
                targetHeight = originHeight
                // 圖片尺寸超過的限制
                if (originWidth > maxWidth || originHeight > maxHeight) {
                  if (originWidth / originHeight > maxWidth / maxHeight) {
                    // 更寬,按照寬度限定尺寸
                    targetWidth = maxWidth
                    targetHeight = Math.round(
                      maxWidth * (originHeight / originWidth)
                    )
                  } else {
                    targetHeight = maxHeight
                    targetWidth = Math.round(
                      maxHeight * (originWidth / originHeight)
                    )
                  }
                }
              }
              canvas.width = targetWidth
              canvas.height = targetHeight
              context.clearRect(0, 0, targetWidth, targetHeight)
              context.fillStyle = '#fff'
              context.drawImage(image, 0, 0, targetWidth, targetHeight) // 繪制 canvas
              
              const canvasURL = canvas.toDataURL('image/jpeg', qualitys)
              const buffer = atob(canvasURL.split(',')[1])
              let length = buffer.length
              const bufferArray = new Uint8Array(new ArrayBuffer(length))
              while (length--) {
                bufferArray[length] = buffer.charCodeAt(length)
              }
              const miniFile = new File([bufferArray], file.name, {
                type: 'image/jpeg'
              })
              console.log({
                file: miniFile,
                origin: file,
                beforeSrc: src,
                afterSrc: canvasURL,
                beforeKB: Number((file.size / 1024).toFixed(2)),
                afterKB: Number((miniFile.size / 1024).toFixed(2)),
                qualitys: qualitys
              })
              resolve({
                file: miniFile,
                origin: file,
                beforeSrc: src,
                afterSrc: canvasURL,
                beforeKB: Number((file.size / 1024).toFixed(2)),
                afterKB: Number((miniFile.size / 1024).toFixed(2))
              })
            }
            image.src = src
          }
          reader.readAsDataURL(file)
        }
      })
    }
  }
}

上述工具返回的是一個(gè)promise對象.
在你封裝好的的uploader.vue組件中使用

import Compress from '@/utilities/compress.js'

關(guān)鍵點(diǎn):因?yàn)楫惒降脑?,我們無法在elementui 上傳組件的 beforeUpload()方法中使用,必須使用自定義的方法上傳,覆蓋掉組件的上傳方法

<template>
  <div class="container">
    <el-upload :multiple="multiple" :accept="types.join(',')"  :show-file-list="showFileList" :action="$root.settings.DOMAIN_APIS.Violet + '/security/upload/temporary'" :data="{ cloud: 'violet', micro: micro}" :headers="{'access-token': $store.getters.access_token,}" :before-upload="beforeUpload" :on-success="onSuccess" :on-error="onError" :on-remove="onRemove">
      <el-button>
        <i class="el-icon-upload"></i>
        {{btnText}}
      </el-button>
    </el-upload>
    <div class="customFileList" v-if="!showFileList">
      <p v-for="(item,index) in fileList" :key=index class="fileList">
        <a :href="$root.settings.DOMAIN_IMG_FILE+item.Url" target="_blank">{{item.Name}}</a>
        <i class="el-icon-delete" @click="deleteButton(item.url)"></i>
      </p>
    </div>
  </div>
</template>
<script>
import {OSS_URL} from '@/apis/violet'

// :http-request="uploadSectionFile"
import Compress from '@/utilities/compress.js'
export default {
  props: {
    // 是否使用ui框架上傳列表,默認(rèn)使用,但無預(yù)覽功能
    showFileList: {
      type: Boolean,
      default: true
    },
    fileList: {
      type: Array,
      default: () => ([])
    },
    types: {
      // 文件類型,默認(rèn)圖片jpeg,jpg,png
      type: Array,
      default: () => (['image/jpeg', 'image/jpg', 'image/png'])
    },
    multiple: {
      type: Boolean,
      default: false
    },
    micro: {
      // 上傳目錄
      type: String,
      default: 'security'
    },
    fileSize: {
      // 文件大小限制 單位MB
      type: Number,
      default: 2
    },
    btnText: {
      type: String,
      default: '上傳文件'
    }
  },
  data() {
    return {
      loading: false,
    }
  },
  methods: {
    uploadFile(copyFile){
        console.log(copyFile)
    },
    uploadSectionFile(params) {
      console.log(params)
      const file = params.file,
        fileType = file.type,
        isImage = fileType.indexOf("image") != -1,
        isLt2M = file.size / 1024 / 1024 < 20
      // 這里常規(guī)檢驗(yàn),看項(xiàng)目需求而定
      if (!isImage) {
        this.$message.error("只能上傳圖片格式png、jpg、gif!");
        return
      }
      if (!isLt2M) {
        this.$message.error("只能上傳圖片大小小于2M");
        return
      }
      // 真正要做壓縮圖片大小的地方
      Compress.compressImg(file).then((res) => {
          console.log(file)
          const copyFile = res.file
           imageUpload(copyFile)
        })
      const imageUpload = (copyFile) => {
        const apiFoo = OSS_URL
         let data={ cloud: 'violet', micro: this.micro,file:copyFile}
           return apiFoo(data).then(res => {
            if (res.data.Code === 'CORRECT') {
                   console.log(res)
            }
          })
      }
      this.loading=true
    },
    beforeUpload(file) {
      if ((file.type && !this.types.includes(file.type)) || (!file.type && !this.types.includes(`.${file.name.split('.')[1]}`))) {
        this.$message.error('請上傳正確文件!')
        return false
      }
      if (file.size / 1024 / 1024 > this.fileSize) {
        this.$message.error(`文件大小不能超過${this.fileSize}MB!`)
        return false
      }


      // 理論上在這個(gè)壓縮圖片大小,實(shí)際上beforeUpload沒有用到,
      // return new Promise((resolve, reject) => {
      //   Compress.compressImg(file).then((res) => {
      //     console.log(res.file)
      //     file = res.file
      //     console.log(file)
      //      // resolve()
      //      const copyFile = file
      //      this.uploadFile(copyFile)
      //      return false
      //   })
      // })
  
      this.loading = true
    },
    onSuccess(res, file) {
      if (res.Code == 'CORRECT') {
        this.getBase64Image(file.raw).then(result => {
          res.Data.Subset.forEach(item => {
            this.$emit('onSuccess', item.Rel, result, file)
          })
        })
      } else {
        this.$message.error(res.Message)
      }
      this.loading = false
    },
    onError() {
      this.loading = false
      this.$message.error('上傳失敗,請稍后重試!')
    },
    onRemove(file) {
      if (file && file.status === 'success') {
        this.$emit('onRemove', file.url)
      }
    },
    getBase64Image(img) {
      return new Promise((calback) => {
        var reader = new FileReader()
        reader.readAsDataURL(img) // 轉(zhuǎn)化二進(jìn)制流,異步方法
        reader.onload = function () { // 完成后this.result為二進(jìn)制流
          calback(this.result)
        }
      })
    },
    deleteButton(url) {
      this.$emit('onRemove', url)
    }
  }
}
</script>
<style lang="scss" scoped>
.upload-img {
  position: relative;
  border: 1px dashed $border-color;
  box-sizing: content-box;
  cursor: pointer;
  &:hover {
    border-color: $light-blue;
  }
  border-radius: 6px;
  /deep/ .el-upload {
    width: 100%;
    height: 100%;
    i {
      display: inline-block;
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      color: #8c939d;
      font-size: $middle-font;
    }
    img {
      display: block;
      width: 100%;
      height: 100%;
    }
  }
  /deep/ .el-loading-spinner {
    margin-top: -25px;
  }
}
/deep/ .fileList {
  display: flex;
  justify-content: space-between;
  .el-icon-delete:hover {
    cursor: pointer;
  }
  .el-icon-delete {
    margin-right: 20px;
  }
}
</style>

額外記錄,與當(dāng)前文章無關(guān)

// violet.js
export const OSS_URL = (parameters) => {
  return fetch({
    cloud: 'Violet',
    url: '/security/upload/temporary',
    method: 'post',
    data: parameters,
  })
}
//fetch.js
  headers: {
    'X-Requested-With': 'XMLHttpRequest',
    // 'Content-Type': 'application/json; charset=utf-8',
    'Content-Type': 'multipart/form-data; boundary=----WebKitFormBoundary5J9Z4y1jAznANNwp',
  }
if (config.method === 'post') {
      //config.data = JSON.stringify(config.data)
      const formdata = new FormData()
      formdata.append('cloud', 'violet'),
      formdata.append('micro', 'agiles'),
      formdata.append('file', config.data.file),
      config.data=formdata
    }
//demandAdd.vue
:fileSize=20 
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容