You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React自定义图片缩放Hook实现求助:抽离图片处理逻辑为useImage

自定义useImage Hook实现图片上传缩放

可以将图片缩放、状态管理及异步处理逻辑封装为可复用的自定义React Hook,以下是具体实现方案:

步骤1:抽离通用辅助函数

将图片格式转换、缩放的核心逻辑单独封装,便于复用和维护:

// 将dataURL转换为File对象
function dataURLtoFile(dataurl, filename) {
  const arr = dataurl.split(',');
  const mime = arr[0].match(/:(.*?);/)[1];
  const bstr = atob(arr[1]);
  let n = bstr.length;
  const u8arr = new Uint8Array(n);
  while (n--) {
    u8arr[n] = bstr.charCodeAt(n);
  }
  return new File([u8arr], filename, { type: mime });
}

// 异步处理图片缩放,返回缩放后的File对象
async function createScaledImageFile(file, maxSize = 224, outputFilename = 'x-ray.png') {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onloadend = (event) => {
      const image = new Image();
      image.onload = () => {
        const canvas = document.createElement('canvas');
        let width = image.width;
        let height = image.height;

        // 计算等比缩放后的尺寸
        if (width > height) {
          width > maxSize && (height *= maxSize / width, width = maxSize);
        } else {
          height > maxSize && (width *= maxSize / height, height = maxSize);
        }

        canvas.width = width;
        canvas.height = height;
        const ctx = canvas.getContext('2d', { alpha: false });
        ctx.drawImage(image, 0, 0, width, height);
        const dataUrl = canvas.toDataURL('image/jpeg', 1);
        resolve(dataURLtoFile(dataUrl, outputFilename));
      };
      image.onerror = reject;
      image.src = event.target.result;
    };
    reader.onerror = reject;
  });
}

步骤2:实现useImage自定义Hook

将状态管理和文件处理逻辑封装到Hook中,对外暴露状态和处理函数:

import { useState } from 'react';

function useImage(maxSize = 224, outputFilename = 'x-ray.png') {
  const [file, setFile] = useState(null);

  const handleChange = async (e) => {
    const selectedFile = e.target.files?.[0];
    if (!selectedFile || !selectedFile.type.match(/image.*/)) return;

    try {
      const scaledFile = await createScaledImageFile(selectedFile, maxSize, outputFilename);
      setFile(scaledFile);
    } catch (err) {
      console.error('图片处理失败:', err);
      // 可在此添加自定义错误提示逻辑
    }
  };

  return [file, handleChange];
}

export { useImage, createScaledImageFile, dataURLtoFile };

步骤3:在组件中使用Hook

在业务组件中直接调用Hook,简化代码逻辑:

import { useImage } from './path-to-your-hook';

function ImageUploadComponent() {
  const [scaledImageFile, handleImageChange] = useImage(224, 'x-ray.png');

  // 提交至后端的逻辑
  const handleSubmit = async () => {
    if (!scaledImageFile) return;
    const formData = new FormData();
    formData.append('image', scaledImageFile);
    // 发起后端请求
    // await fetch('/api/upload', { method: 'POST', body: formData });
  };

  return (
    <div>
      <input type="file" accept="image/*" onChange={handleImageChange} />
      <button onClick={handleSubmit} disabled={!scaledImageFile}>上传图片</button>
    </div>
  );
}

优化亮点

  • 用async/await替代多层回调,提升代码可读性
  • Hook支持自定义maxSize和outputFilename,增强复用灵活性
  • 添加错误捕获逻辑,避免图片处理失败导致组件崩溃
  • 辅助函数与Hook解耦,可单独用于其他场景

内容的提问来源于stack exchange,提问作者Weber Chang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 14:54:24