【React】使用browser-image-compression在上传前压缩图片、react上传图片压缩

文章目录


前言

下载依赖

bash 复制代码
npm install browser-image-compression -S

我这里是react 使用,本质上vue也是相同的用法,都是js,以及input调用

用的input标签进行上传,原汁原味,不使用组件,因为组件库界面不喜欢。

一、编写utils方法

utils/common.ts文件里面写公共方法

js 复制代码
import imageCompression from 'browser-image-compression';


/**
 * 压缩图片文件
 * @param file - 要压缩的图片文件
 * @param maxSizeMB - 最大文件大小(MB),超过此大小才压缩,默认 1MB
 * @returns Promise<File> - 返回压缩后的图片文件(如果小于 maxSizeMB 则返回原文件)
 */
export async function compressImage(file: File, maxSizeMB: number = 1): Promise<File> {
  // 检查文件大小(MB)
  const fileSizeMB = file.size / 1024 / 1024;

  // 如果文件小于等于 maxSizeMB,不需要压缩,直接返回原文件
  if (fileSizeMB <= maxSizeMB) {
    return file;
  }

  // 根据文件大小动态设置压缩选项
  // 注意:browser-image-compression 默认保持宽高比(等比例压缩)
  const options = {
    maxSizeMB: maxSizeMB, // 目标文件大小(MB)
    maxWidthOrHeight: 1920, // 最大宽度或高度(会等比例缩放,保持宽高比)
    useWebWorker: true, // 使用 Web Worker 进行压缩(不阻塞主线程)
    fileType: file.type, // 保持原文件类型
    // 压缩质量(0-1),如果不设置,库会根据 maxSizeMB 自动调整
    // initialQuality: 0.8, // 可选:初始压缩质量
  };

  try {
    const compressedFile = await imageCompression(file, options);
    console.log(
      `图片压缩完成: ${file.name}, 原始大小: ${fileSizeMB.toFixed(2)}MB, 压缩后: ${(compressedFile.size / 1024 / 1024).toFixed(2)}MB`
    );
    return compressedFile;
  } catch (error) {
    console.error('图片压缩失败:', error);
    // 压缩失败时返回原文件
    return file;
  }
}

二、使用步骤

1.标签使用

代码如下(示例):

js 复制代码
{/* 添加图片按钮 */}
<label className="block w-full border-2 border-dashed border-input rounded-lg cursor-pointer hover:border-primary transition-colors p-6 text-center">
  <input
    type="file"
    accept="image/png,image/jpeg,image/jpg"
    multiple
    className="hidden"
    onChange={async (e) => {
      const fileList = e.target.files;
      if (!fileList || fileList.length === 0) return;
      // 将 FileList 转换为数组
      const files = Array.from(fileList);
      // 验证所有文件类型
      const invalidFiles: File[] = [];
      for (let i = 0; i < files.length; i++) {
        const file = files[i];
        if (!file.type.match(/^image\/(png|jpeg|jpg)$/)) {
          invalidFiles.push(file);
        }
      }
      if (invalidFiles.length > 0) {
        alert('请上传 png、jpeg 或 jpg 格式的图片');
        e.target.value = '';
        return;
      }
      try {
        // 压缩图片(如果大于 1MB)
        const compressedFiles = await Promise.all(files.map((file) => compressImage(file, 1)));
        // 构建 FormData,使用动态下标
        const uploadFormData = new FormData();
        for (let i = 0; i < compressedFiles.length; i++) {
          uploadFormData.append(`images[${i}][file]`, compressedFiles[i]);
          uploadFormData.append(`images[${i}][subPath]`, 'product/goods');
        }
        // 上传图片
        CommonApi.uploadImages(uploadFormData)
          .then((res) => {
            if (Array.isArray(res.data)) {
              const newAssets = res.data
                .map((item: any, index: number) => {
                  if (item?.path) {
                    return {
                      scope: '',
                      file_name: files[index]?.name || '',
                      file_path: item.path,
                      sort_order: formData.assets.length + index,
                      alt_text: '',
                    };
                  }
                  return null;
                })
                .filter((asset): asset is NonNullable<typeof asset> => asset !== null);
              if (newAssets.length > 0) {
                // 执行赋值操作
              } else {
                console.error('上传图片接口返回的数据格式不正确:', res.data);
                alert('上传图片失败,请重试');
              }
            } else {
              console.error('上传图片接口返回的数据格式不正确:', res.data);
              alert('上传图片失败,请重试');
            }
          })
          .catch((error) => {
            console.error('上传图片失败: ', error);
            alert('上传图片失败,请重试');
          });
      } catch (error) {
        console.error('压缩图片失败: ', error);
        alert('处理图片失败,请重试');
      }
      // 清空input,允许重复上传同一文件
      e.target.value = '';
    }}
    ... 这里写你上传按钮或者图片图标
  />

2.分析

  1. label标签和input标签是联动的,把input隐藏, label内写其他的div,就可以做到点击触发上传
  2. 这是我的上传格式
  3. 返回值res.data是一个数组,
    如下
js 复制代码
{
    "ret": 1,
    "msg": "success",
    "data": [
        {
            "name": "20251202zO8PwOslh7U19qNW40Tf7sNjYOkRNHGLKeXxNacl.jpg",
            "path": "\/product\/goods\/20251202zO8PwOslh7U19qNW40Tf7sNjYOkRNHGLKeXxNacl.jpg",
            "width": 1200,
            "height": 1200
        }
    ]
}

使用compressImage别忘记了引入


总结

提示:这里对文章进行总结:

例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。

相关推荐
梦梦代码精1 分钟前
开源、免费、可商用:BuildingAI一站式体验报告
开发语言·前端·数据结构·人工智能·后端·开源·知识图谱
0思必得08 分钟前
[Web自动化] Selenium执行JavaScript语句
前端·javascript·爬虫·python·selenium·自动化
程序员敲代码吗23 分钟前
MDN全面接入Deno兼容性数据:现代Web开发的“一张图”方案
前端
0思必得024 分钟前
[Web自动化] Selenium截图
前端·爬虫·python·selenium·自动化
tb_first1 小时前
SSM速通2
java·javascript·后端
疯子****1 小时前
【无标题】
前端·clawdbot
RichardLau_Cx2 小时前
【保姆级实操】MediaPipe SDK/API 前端项目接入指南(Web版,可直接复制代码)
前端·vue·react·webassembly·mediapipe·手部追踪·前端计算机视觉
不爱写程序的东方不败2 小时前
APP接口测试流程实战Posman+Fiddler
前端·测试工具·fiddler
晚霞的不甘3 小时前
Flutter for OpenHarmony构建全功能视差侧滑菜单系统:从动效设计到多页面导航的完整实践
前端·学习·flutter·microsoft·前端框架·交互
黎子越3 小时前
python相关练习
java·前端·python