vue3+element-plus图片上传,前端压缩(纯函数,无插件)

方法:直接复制

javascript 复制代码
// 核心压缩方法(纯函数,无this)
// ✅ 优化后的【可用】压缩方法(替换你原有代码里的同名方法)
const imageCompress = async (file, quality = 0.7, maxWidth = 1920) => {
  return new Promise((resolve, reject) => {
    if (!file || !file.type.startsWith('image/')) {
      reject(new Error('非图片文件,无需压缩'));
      return;
    }
    const img = new Image()
    img.setAttribute('crossOrigin', 'anonymous')
    const originalUrl = URL.createObjectURL(file)
    img.src = originalUrl

    // 图片加载失败容错
    img.onerror = () => {
      URL.revokeObjectURL(originalUrl);
      reject(new Error('图片加载失败,无法压缩'));
    };

    img.onload = () => {
      let { width, height } = img
      // 等比例缩放,避免变形
      if (width > maxWidth) {
        const scale = maxWidth / width
        width = maxWidth
        height = height * scale
      }

      const canvas = document.createElement('canvas')
      const ctx = canvas.getContext('2d')
      canvas.width = width
      canvas.height = height
      ctx.clearRect(0, 0, width, height)
      ctx.drawImage(img, 0, 0, width, height)

      canvas.toBlob(
        (blob) => {
          if (!blob) {
            reject(new Error('图片压缩失败'));
            return;
          }
          // 生成压缩后的File文件,保留原文件名/格式
          const compressFile = new File([blob], file.name, { type: file.type })
          const compressUrl = URL.createObjectURL(blob)
          resolve({
            originalUrl,
            compressUrl,
            originalSize: file.size,
            compressSize: blob.size,
            compressFile // ✅ 核心:压缩后的文件对象,用于后续上传
          })
        },
        file.type,
        quality // 0.7是9M图片的黄金值:体积压至800KB左右,画质无明显损失
      )
      URL.revokeObjectURL(originalUrl)
    }
  })
}

调用:图片上传的beforeUpload方法中使用

javascript 复制代码
const beforeUpload = async (file) => {
  // 1. 原有格式校验:保留不变
  const isImage = ['image/jpeg', 'image/png'].includes(file.type)
  const isLt10M = file.size / 1024 / 1024 <= 10

  if (!isImage) {
    ElMessage.error('只能上传JPG/PNG格式图片!')
    return false // 校验失败,终止上传
  }
  if (!isLt10M) {
    ElMessage.error('图片大小不能超过10M!')
    return false // 校验失败,终止上传
  }

  // 2. 校验通过 → 执行图片压缩(核心新增逻辑)
  try {
    console.log(`开始压缩【${file.name}】,原大小:${(file.size/1024/1024).toFixed(2)}MB`)
    const compressRes = await imageCompress(file, 0.7, 1920)
    console.log(`压缩完成,新大小:${(compressRes.compressSize/1024/1024).toFixed(2)}MB`)
    ElMessage.success(`图片压缩成功,体积减小${Math.round((1 - compressRes.compressSize/file.size)*100)}%`)
    
    // ✅ 返回压缩后的文件 → el-upload会自动用这个文件继续上传流程
    return compressRes.compressFile
  } catch (err) {
    ElMessage.error(`图片压缩失败:${err.message},将上传原图`)
    return file // 压缩失败兜底,上传原图
  }
}

实测:8M左右压缩到300k-500k左右,具体大小可以自己按压缩比例调整

相关推荐
憧憬成为web高手5 小时前
ACTF 12307复现
前端·bootstrap·html
wordbaby5 小时前
Axios 上传大文件崩溃:鸿蒙 RNOH 下 XHR 返回空响应头引发的"假失败"
前端·react native
wordbaby6 小时前
React Native 列表分页实战:下拉刷新与上拉加载的工程化方案
前端·react native
wordbaby6 小时前
脱离 Tab 栏的艺术:React Native 全屏子页面的导航架构实践
前端·react native·harmonyos
陈随易7 小时前
Redis 8.8发布,一定要更新
前端·后端·程序员
wordbaby7 小时前
React Native 新架构落地鸿蒙:跨三端政务级应用的工程实践与深度复盘
前端·react native·harmonyos
晓说前端7 小时前
第一篇:为什么学TypeScript?—— 优势、场景与环境搭建
javascript·ubuntu·typescript
excel8 小时前
为什么我推荐使用 Termius:现代 SSH 工具的完整体验
前端·后端
ZC跨境爬虫8 小时前
模块化烹饪小程序开发日记 Day7:(菜谱详情接口开发与JSON数据读取全流程)
前端·javascript·css·ui·微信小程序·json
এ慕ོ冬℘゜8 小时前
JS 前端基础面试题
开发语言·前端·javascript