美文网首页
vue+elementui+web上传图片压缩

vue+elementui+web上传图片压缩

作者: 小话001 | 来源:发表于2021-06-08 22:33 被阅读0次

    注意事项:未解决PNG带通道透明图片压缩后黑屏的问题,网上有评论说绘制背景色白色

    context.fillStyle = '#fff'

    个人觉得意义不大,因为只要是做设计的都明白透明通道和白色背景不是同一个概念。
    为啥不支持PNG?
    点击此处查看原因

    原因总结:会用到toDataURL('image/jpeg',qualitys)的一个方法

    在指定图片格式为 image/jpeg 或 image/webp的情况下,可以从 0 到 1 的区间内选择图片的质量。如果超出取值范围,将会使用默认值 0.92,其他参数会被忽略。
    文件工具: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 数组返回 Promise 数组
        } else {
          return new Promise(resolve => {
            // console.log(file)
            if ((file.size / 1024).toFixed(2) < 300) {
              resolve({
                file: file
              })
            } else {
              const reader = new FileReader() // 创建 FileReader
              reader.onload = ({ target: { result: src } }) => {
                const image = new Image() // 创建 img 元素
                image.onload = async () => {
                  const canvas = document.createElement('canvas') // 创建 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)
            }
          })
        }
      }
    }
    
    

    上述工具返回的是一个promise对象.
    在你封装好的的uploader.vue组件中使用

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

    关键点:因为异步的原因,我们无法在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框架上传列表,默认使用,但无预览功能
        showFileList: {
          type: Boolean,
          default: true
        },
        fileList: {
          type: Array,
          default: () => ([])
        },
        types: {
          // 文件类型,默认图片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
          // 这里常规检验,看项目需求而定
          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
          }
    
    
          // 理论上在这个压缩图片大小,实际上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) // 转化二进制流,异步方法
            reader.onload = function () { // 完成后this.result为二进制流
              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>
    

    额外记录,与当前文章无关

    // 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 
    

    相关文章

      网友评论

          本文标题:vue+elementui+web上传图片压缩

          本文链接:https://www.haomeiwen.com/subject/xfijeltx.html