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
相关产品推荐
相关产品推荐

