【Element Plus】图片上传及压缩

702 阅读2分钟

由于页面列表显示较大图片时会导致卡顿或者图片显示加载较慢,给用户带来不好的体验,因此需要在图片上传时进行压缩处理(为什么不在后端多生成一张压缩图?别问,问就是后端没空(╯‵□′)╯︵┻━┻)。产品的需求是将上传图片的最长边压缩到不超过 maxImageSide(例如 1024 像素),本文将介绍如何使用 Vue 3、TypeScript 以及 Element Plus 来实现一个简单的图片上传和压缩功能。

实现步骤

1. 创建上传组件

<template>
  <el-upload
    :show-file-list="false"
    :before-upload="beforeUpload"
    accept=".jpg,.jpeg,.png"
  >
    <el-button type="primary">上传图片</el-button>
  </el-upload>
  <div v-if="imageSrc">
    <img :src="imageSrc" alt="Compressed Image" />
  </div>
</template>

2. 上传前校验

  • 文件类型和大小验证:在文件上传前,我们首先检查文件类型是否在接受的范围内,并验证文件大小是否超过限制。
/** 上传拦截 */
const beforeUpload = function (file: File) {
  const fileType = getFileExtension(file.name)
  const isImage = ['.jpg', '.png', '.jpeg'].includes(fileType)
  const isAcceptedType = props.accept.includes(fileType)
  const isSizeValid = file.size / 1024 / 1024 <= props.size

  // 1.文件类型限制.
  if (!isAcceptedType) {
    ElMessage.error(`上传类型只能为 ${props.accept}`)
    return false
  }
  // 2.文件大小限制
  if (!isSizeValid) {
    ElMessage.error(`文件大小不能超过 ${props.size}M`)
    return false
  }
  // 3. 非压缩 | 非图片,上传文件
  if (!props.thumbnail || !isImage) {
    loading.value = true
    return true
  }
  // 4. 压缩图片,限制长边为 maxImageSide
  loading.value = true
  return compressImage(file)
}

  • 压缩逻辑:使用 FileReader 读取文件并转换为 Base64 字符串后,通过 <canvas> 对图像进行压缩。
/** 获取文件后缀 */
const getFileExtension = (fileName: string) => {
  return fileName.substring(fileName.lastIndexOf('.')).toLowerCase()
}
/** 压缩图片 */
const compressImage = (file: File) => {
  return new Promise(resolve => {
    const reader = new FileReader()
    reader.onload = event => {
      const img = new Image()
      img.src = event.target?.result as string
      img.onload = () => {
        const { width, height } = img

        // 若图片长宽不超过maxImageSide,不需压缩
        if (width <= maxImageSide && height <= maxImageSide) {
          resolve(true)
          return
        }

        const isWidthLonger = width > height
        const newWidth = isWidthLonger ? maxImageSide : (width / height) * maxImageSide
        const newHeight = isWidthLonger ? (height / width) * maxImageSide : maxImageSide

        const canvas = document.createElement('canvas')
        canvas.width = newWidth
        canvas.height = newHeight

        const ctx = canvas.getContext('2d')
        ctx?.drawImage(img, 0, 0, newWidth, newHeight)

        const dataUrl = canvas.toDataURL(file.type)
        const blobData = dataURItoBlob(dataUrl, file.type)
        resolve(blobData)
      }
    }
    reader.readAsDataURL(file)
  })
}

  • 转换为 Blob:将压缩后的 Base64 数据转换为 Blob,以便于上传到服务器。
/** 图片base64转Blob格式 */
function dataURItoBlob(dataURI: string, type: string) {
  const binary = atob(dataURI.split(',')[1])
  const array = new Uint8Array(binary.length)
  for (let i = 0; i < binary.length; i++) {
    array[i] = binary.charCodeAt(i)
  }
  return new Blob([array], { type })
}

实现效果

​编辑

以上就是图片上传及压缩的实现啦^-^