Expo/React Native上传Form Data图片时文件大小为0的问题
Expo/React Native 上传图片后端接收文件大小为0的解决方案
问题背景
Postman调用后端接口可正常将图片上传至Cloudinary,但Expo/React Native前端上传时,后端返回500错误,提示文件为空(大小:0)。前端控制台打印的Form Data显示文件大小非零,后端能接收到正确数量的文件,但所有文件内容均为空。
前端FormData日志
formData [{"_data": {"__collector": [Object], "blobId": "a89556eb-d358-4fc5-846f-bb9a55292d3e", "lastModified": undefined, "name": "C6777AC5-64A4-4A03-8920-D7B2E2325345.jpg", "offset": 0, "size": 3566999, "type": "image/jpeg"}}, {"_data": {"__collector": [Object], "blobId": "6ca57385-48e8-450e-af7e-4dcd21844788", "lastModified": undefined, "name": "ABD5D206-8580-468C-9EDA-6C8D6F29F254.jpg", "offset": 0, "size": 4455509, "type": "image/jpeg"}}]
原前端核心代码
const handleUploadToCloudinaryWithBlob2 = async () => { if (selectedAssets.length === 0) { console.log("selectedAssets has a length of 0"); return; } const formData = new FormData(); for (const asset of selectedAssets) { const fileNameParts = asset.uri.split("."); const fileExtension = fileNameParts[fileNameParts.length - 1]; const fileName = asset.uri.split("/").pop()?.split(".")[0]; const imageBlob = await fetch(asset.uri).then((response) => response.blob() ); console.log("imageBlob", imageBlob); console.log("imageBlob.type", imageBlob.type); console.log("imageBlob.size", imageBlob.size); const fileType = fileExtension === "jpg" ? "jpeg" : fileExtension; const blobFile = new File( [imageBlob], `${fileName}.${fileExtension}` || `image-${Date.now()}.${fileType}`, { type: `image/${fileType}` } ); formData.append(`files`, blobFile, `${fileName}.${fileExtension}`); } console.log("formData", formData.getAll("files")); try { const response = await axios.post( `${process.env.EXPO_PUBLIC_SERVER_URL}/posts/upload/cloudinary`, formData, { headers: { Accept: "application/json", "Content-Type": "multipart/form-data", }, onUploadProgress: (progressEvent) => { if (progressEvent.total) { const progress = Math.round( (progressEvent.loaded * 100) / progressEvent.total ); console.log(progress); } }, } ); console.log("Response:", response.data); } catch (error) { console.error("Error uploading images:", error); } };
问题原因
- 手动设置
Content-Type: multipart/form-data:axios会自动为FormData生成包含boundary的Content-Type,手动设置会丢失boundary,导致后端无法正确解析文件内容。 - Expo本地URI转Blob的兼容性问题:直接用
fetch处理Expo的本地文件URI(file://开头),在部分环境下无法正确读取文件内容,导致生成的Blob为空。
修复方案
1. 安装依赖
先安装expo-file-system用于可靠读取本地文件:
npx expo install expo-file-system
2. 修改上传函数
替换原handleUploadToCloudinaryWithBlob2函数:
import * as FileSystem from 'expo-file-system'; const handleUploadToCloudinaryWithBlob2 = async () => { if (selectedAssets.length === 0) { console.log("selectedAssets has a length of 0"); return; } const formData = new FormData(); for (const asset of selectedAssets) { const fileNameParts = asset.uri.split("."); const fileExtension = fileNameParts[fileNameParts.length - 1]; const fileName = asset.uri.split("/").pop()?.split(".")[0] || `image-${Date.now()}`; const fileType = fileExtension === "jpg" ? "jpeg" : fileExtension; // 用FileSystem读取base64再转Blob,避免fetch的兼容性问题 const base64 = await FileSystem.readAsStringAsync(asset.uri, { encoding: FileSystem.EncodingType.Base64, }); const blob = await fetch(`data:image/${fileType};base64,${base64}`).then(res => res.blob()); const file = new File([blob], `${fileName}.${fileExtension}`, { type: `image/${fileType}`, }); formData.append('files', file); } console.log("formData", formData.getAll("files")); try { const response = await axios.post( `${process.env.EXPO_PUBLIC_SERVER_URL}/posts/upload/cloudinary`, formData, { headers: { Accept: "application/json", // 移除手动设置的Content-Type,让axios自动生成 }, onUploadProgress: (progressEvent) => { if (progressEvent.total) { const progress = Math.round( (progressEvent.loaded * 100) / progressEvent.total ); console.log(progress); } }, } ); console.log("Response:", response.data); } catch (error) { console.error("Error uploading images:", error); } };
3. 关键修改点
- 移除了手动设置的
Content-Type: multipart/form-data,让axios自动生成带boundary的请求头 - 改用
FileSystem.readAsStringAsync读取本地文件的base64,再转成Blob,确保文件内容被正确读取 - 简化了文件名和类型的处理逻辑
验证
重新运行Expo项目,选择图片后点击上传,后端应该能正常接收到非空的文件内容,完成Cloudinary上传。
内容的提问来源于stack exchange,提问作者noahreeves
相关产品推荐
相关产品推荐

