React Native fetch上传Blob/File至服务器失败问题排查
问题定位
你遇到的上传失败、报Network request failed、服务器收不到附件的问题,核心是两个写法错误,和权限、第三方服务SDK没有关系:
- 错误1:手动给请求写死了
Content-Type: multipart/form-data请求头。React Native/Expo环境下发送FormData请求时,必须让框架自动生成这个请求头——自动生成的头会附带随机生成的boundary分隔符,用来标记表单里不同字段的边界。你手动写死头之后没有这个分隔符,服务器完全无法解析表单结构,自然识别不到附件,会把请求判定为空请求。 - 错误2:用
fetch(本地uri).blob()的方式转文件格式。Expo内置的fetch对本地file://协议资源的blob转换存在已知兼容问题,转出来的文件大概率是空值或者损坏格式,你注释掉的直接传uri配置对象的写法才是RN生态的标准实现,TS的类型报错是因为官方类型定义没有覆盖RN原生层支持的传参格式,直接忽略即可,不是写法错误。
修复方案
按以下步骤调整代码即可:
- 第一步:删除请求头中手动配置的
"Content-Type": "multipart/form-data"字段,Accept字段可以保留,不要手动干预表单请求的Content-Type生成。 - 第二步:移除你写的
fetchImageFromUri转blob的逻辑,换回直接传递文件元信息对象的写法,注意不要裁剪ImagePicker返回的uri前缀,iOS返回的file://开头路径要完整传入。 - 第三步:如果使用Expo SDK 48及以上版本,只需要在app.json中配置iOS的相册权限描述即可,不需要加任何针对Android 5.0以下版本的过时存储权限配置,那些5年以上的老教程内容完全不适配当前版本。
修复后的核心上传代码如下:
const upload = async () => { setMessage(""); setErrMessage(""); if (pathToImage != null) { const formData = new FormData(); formData.append("action", "Image Upload"); // 直接传递文件配置对象,忽略TS类型报错 const uriParts = pathToImage.split("."); const fileType = uriParts[uriParts.length - 1]; formData.append("image", { uri: pathToImage, name: `photo.${fileType}`, type: `image/${fileType}`, // @ts-ignore RN原生层支持该传参格式,TS类型定义未覆盖 } as any); const options: RequestInit = { method: "POST", body: formData, headers: { Accept: "application/json", // 不要手动设置multipart/form-data头,让框架自动生成 }, }; try { const res = await fetch(URL, options); if (!res.ok) { throw new Error("服务端返回异常状态"); } const body = (await res.json()) as any; if (body.code > 200) { setErrMessage(body.msg); } else { setMessage(body.msg); } } catch (err: any) { setErrMessage("上传失败,请重试"); console.log("upload catch error:", err.message); } } };
补充排查点
如果改完代码仍有问题,按以下顺序排查:
- 检查请求地址是否为HTTP明文地址,Android 9以上版本默认禁止明文HTTP请求,要么换成HTTPS地址,要么在配置中开启明文流量允许。
- 真机调试时不要用
localhost作为服务端地址,localhost指向的是手机设备本身,要换成你开发机所在局域网的实际IP。 - 不要引入第三方form-data的polyfill包,Expo/RN内置的FormData实现已经足够,额外引入polyfill会导致boundary生成逻辑冲突,触发同样的报错。
内容的提问来源于stack exchange,提问作者Chip
相关产品推荐
相关产品推荐

