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

相关推荐
橙序员小站1 小时前
Agent Skill 是什么?一文讲透 Agent Skill 的设计与实现
前端·后端
炫饭第一名4 小时前
速通Canvas指北🦮——基础入门篇
前端·javascript·程序员
王晓枫4 小时前
flutter接入三方库运行报错:Error running pod install
前端·flutter
符方昊4 小时前
React 19 对比 React 16 新特性解析
前端·react.js
ssshooter4 小时前
又被 Safari 差异坑了:textContent 拿到的值居然没换行?
前端
曲折4 小时前
Cesium-气象要素PNG色斑图叠加
前端·cesium
Forever7_4 小时前
Electron 淘汰!新的桌面端框架 更强大、更轻量化
前端·vue.js
不会敲代码15 小时前
前端组件化样式隔离实战:React CSS Modules、styled-components 与 Vue scoped 对比
css·vue.js·react.js
Angelial5 小时前
Vue3 嵌套路由 KeepAlive:动态缓存与反向配置方案
前端·vue.js
jiayu5 小时前
Angular学习笔记24:Angular 响应式表单 FormArray 与 FormGroup 相互嵌套
前端