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

Expo React Native安卓端Fetch Multipart图片上传失败,iOS正常

安卓端Expo React Native Fetch上传FormData图片失败的解决方案

针对你遇到的安卓端图片上传报TypeError: Network request failed但iOS正常的问题,以下是几个常见原因和对应的解决步骤:

1. 安卓URI格式兼容性问题

Expo中安卓设备获取的图片URI通常是content://开头的,Fetch在处理这类URI时可能存在兼容性问题,导致无法正确读取文件内容。

解决方法:

使用expo-file-system将图片复制到缓存目录,使用缓存目录的URI进行上传:

import * as FileSystem from 'expo-file-system';
import { Platform } from 'react-native';

const callUploadApi = async () => {
  try {
    // 处理安卓URI
    let uploadUri = imgUri;
    if (Platform.OS === 'android' && uploadUri.startsWith('content://')) {
      // 复制图片到缓存目录
      const fileName = imgUri.split('/').pop();
      const cacheUri = FileSystem.cacheDirectory + fileName;
      await FileSystem.copyAsync({ from: imgUri, to: cacheUri });
      uploadUri = cacheUri;
    }

    const imageType = uploadUri.endsWith(".jpg")
      ? "image/jpeg"
      : uploadUri.endsWith(".png")
      ? "image/png"
      : uploadUri.endsWith(".gif")
      ? "image/gif"
      : "image/jpeg";
    
    const formdata = new FormData();
    formdata.append("file", {
      uri: Platform.OS === 'android' ? uploadUri.replace('file://', '') : uploadUri, // 安卓去掉file://前缀
      type: imageType,
      name: uploadUri.split("/").pop(),
    });
    
    const requestOptions = {
      method: "POST",
      body: formdata,
      redirect: "follow",
    };

    const response = await fetch(`${SERVER_URL}/api/upload`, requestOptions);

    if (!response.ok) {
      const errorResponse = await response.json();
      throw errorResponse;
    }

    const result = await response.json();
    const { fileUrl } = result.data;
    setImgUrl(fileUrl);

    Alert.alert("Upload Success", "Image uploaded successfully");
  } catch (error) {
    console.error(error);
    Alert.alert("Upload Failed", "Image upload failed");
  }
};

2. 动态权限未正确申请

虽然你检查了app.json中的权限,但安卓13+需要动态申请READ_MEDIA_IMAGES权限,旧版本需要READ_EXTERNAL_STORAGE,仅在配置文件中声明权限无法完成授权流程。

解决方法:

在调用图片选择或上传接口前,动态申请媒体权限:

import * as ImagePicker from 'expo-image-picker';
import { Alert } from 'react-native';

const requestMediaPermission = async () => {
  const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
  if (status !== 'granted') {
    Alert.alert('权限不足', '请授予应用访问媒体库的权限,否则无法上传图片');
    return false;
  }
  return true;
};

// 在调用callUploadApi前先执行权限检查
const handleUpload = async () => {
  const hasPermission = await requestMediaPermission();
  if (hasPermission) {
    await callUploadApi();
  }
};

3. 服务器端对安卓请求的兼容性问题

安卓上传的FormData请求头或文件名格式可能与iOS略有差异,导致服务器无法解析:

  • 检查服务器日志,确认安卓请求是否到达服务器,若到达,查看是否有解析Multipart数据的错误
  • 尝试将文件名改为纯英文格式,避免中文或特殊字符:
    name: `upload_image.${imageType.split('/')[1]}` // 替换原name字段
    
  • 确认服务器的client_max_body_size(如Nginx配置)足够大,避免因图片过大被拦截

4. Fetch请求头的隐性问题

虽然Fetch会自动为FormData设置multipart/form-data头,但安卓端有时需要显式添加Accept头,确保服务器返回正确格式的响应:

const requestOptions = {
  method: "POST",
  body: formdata,
  headers: {
    Accept: 'application/json',
  },
  redirect: "follow",
};

注意:不要手动设置Content-Type为multipart/form-data,否则会丢失自动生成的boundary参数,导致服务器无法解析表单数据。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 06:26:03