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

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);
  }
};

问题原因

  1. 手动设置Content-Type: multipart/form-data:axios会自动为FormData生成包含boundary的Content-Type,手动设置会丢失boundary,导致后端无法正确解析文件内容。
  2. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 20:54:57