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左右,具体大小可以自己按压缩比例调整

相关推荐
Moment3 分钟前
牛逼,NextJs 从 16.3 开始全面拥抱 Agent Native 🥰🥰🥰
前端·后端·面试
沸点小助手24 分钟前
6月沸点活动获奖名单公示|本周互动话题上新🎊
前端·后端
Csvn31 分钟前
React 19 `use()` 来了:以后数据加载可以不用 useEffect?
前端·react.js
没落英雄34 分钟前
从零开始搭建一个 AI Agent —— LangChain + TypeScript 实战手记
前端·人工智能·架构
远航_36 分钟前
git submodule
前端·后端·github
摸着石头过河的石头38 分钟前
从 Webpack 到 RSBuild:前端构建工具的进化之路
前端
疯狂的魔鬼39 分钟前
告别 boolean 地狱:一个文件上传组件的状态机实践
前端·设计
竹林81839 分钟前
Solana DApp 开发踩坑实录:从零用 @solana/web3.js 实现链上数据查询与交易签名
前端·javascript
狂师43 分钟前
测试工程师的AI 技能库:推荐5个让你效率翻倍的Skills
前端·后端·测试
李明卫杭州43 分钟前
Vue3 watch 与 watchEffect 深度解析
前端