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

React实现图片转Blob并发送至服务端的方法咨询

React 本地图片转Blob上传实现方案

核心问题修正

你当前代码存在两个认知误区:

  • <input type="file"> 选中文件后返回的File对象本身就是Blob的子类,完全符合fetch请求body的参数要求,不需要额外做格式转换
  • 你现在写的blobFile方法通过readAsDataURL读取到的是base64编码的字符串,不是Blob对象,无法直接用于二进制上传。

推荐实现(零多余转换,性能最优)

不需要用FileReader转DataURL,直接存储选中的File对象即可,预览用URL.createObjectURL生成临时地址,比DataURL内存占用低很多。
完整可运行代码:

const { useState, useEffect } = React;

const App = () => {
  // 存储可直接上传的Blob(File)对象
  const [imageBlob, setImageBlob] = useState(null);
  // 存储预览用临时地址
  const [previewUrl, setPreviewUrl] = useState('');

  // 对应你代码里的getImage方法
  const getImage = () => imageBlob;

  const handleFileChange = (file) => {
    // 清除之前的预览地址,释放内存
    if (previewUrl) URL.revokeObjectURL(previewUrl);

    if (!file) {
      setImageBlob(null);
      setPreviewUrl('');
      return;
    }

    setImageBlob(file);
    // 生成预览临时地址
    setPreviewUrl(URL.createObjectURL(file));
  };

  const sendToServer = async () => {
    const pollUrl = `api/...`;
    const blob = await getImage();
    if (!blob) {
      console.error('请先选择要上传的图片');
      return;
    }

    try {
      const res = await fetch(pollUrl, {
        method: "POST",
        // 手动设置正确的MIME类型,避免服务端识别失败
        headers: {
          "Content-Type": blob.type
        },
        body: blob,
      });
      const data = await res.text();
      if (res.ok) console.log("上传成功", data);
      else throw new Error(data);
    } catch (e) {
      console.error('上传失败:', e);
    }
  };

  // 组件卸载时释放预览地址内存
  useEffect(() => {
    return () => {
      if (previewUrl) URL.revokeObjectURL(previewUrl);
    }
  }, [previewUrl]);

  return (
    <div>
      <img width="200" height="200" src={previewUrl} alt="avatar"/>
      <input 
        type="file" 
        accept="image/*"
        onChange={(event) => handleFileChange(event.target.files[0] || null)} 
      />
      <button onClick={sendToServer} disabled={!imageBlob}>上传图片</button>
    </div>
  );
}

ReactDOM.render(
  <App/>,
  document.getElementById('root')
);

对应的HTML运行环境:

<script src="https://unpkg.com/react/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<div id="root"></div>

特殊场景转换方法

如果你后续需要做图片压缩、裁剪(比如把图片画到Canvas上编辑后再上传),或者确实拿到了DataURL需要转Blob,可以用下面的工具方法:

DataURL转Blob

const dataURLtoBlob = (dataUri) => {
  const [meta, data] = dataUri.split(',');
  const mime = meta.match(/:(.*?);/)[1];
  const binaryStr = atob(data);
  const u8Arr = new Uint8Array(binaryStr.length);
  for (let i = 0; i < binaryStr.length; i++) {
    u8Arr[i] = binaryStr.charCodeAt(i);
  }
  return new Blob([u8Arr], { type: mime });
};

Canvas导出Blob

不要把Canvas转成DataURL再转Blob,直接用原生API导出,性能更好:

const getCanvasBlob = (canvas) => new Promise((resolve) => {
  canvas.toBlob((blob) => resolve(blob), 'image/png', 0.9);
});

常见踩坑提示

  • 如果服务端要求multipart/form-data格式(比如需要同时传其他表单字段、文件名),不要直接传Blob,把Blob放到FormData中即可,此时不要手动设置Content-Type请求头,浏览器会自动带上正确的分隔符:
    const formData = new FormData();
    formData.append('avatar', imageBlob, 'user-avatar.png');
    const res = await fetch(pollUrl, {
      method: "POST",
      body: formData
    });
    
  • 尽量避免File -> DataURL -> Blob的多余转换:base64编码会让文件体积增大33%左右,大图片场景下会阻塞JS主线程,造成页面卡顿。
  • 用URL.createObjectURL生成的预览地址一定要在不需要的时候调用URL.revokeObjectURL释放,否则会造成内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.02 05:00:44