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

使用React Native、GraphQL和urql上传图片失败求助

React Native + GraphQL 图片上传失败问题处理记录

问题现象

使用React Native结合urql发起GraphQL mutation上传头像时,始终返回错误:[CombinedError: [Network] Network request failed]。服务器要求picture参数为MultipartFile类型,若传入非File类型数据,会收到明确报错:Variable 'picture' has an invalid value: Expected type org.springframework.web.multipart.MultipartFile but was java.util.LinkedHashMap]。

已尝试的无效方案

  • 将图片转为Base64后构建Blob,再生成File对象传入mutation
  • 尝试用FormData、react-native-blob-util、rn-fetch-blob等工具处理文件上传
  • 给请求添加graphql-require-preflight请求头

相关代码片段

GraphQL Mutation定义

import { graphql } from '../../gql';
import { gql, useMutation } from 'urql';

const AddProfilePicture_Mutation = graphql(`
mutation AddPicture_Mutation ($userId: ID!, $picture: Upload!) {
  uploadProfilePicture(input: {
    userId: $userId
    picture: $picture
  }) {
    id
  }
}`);

export const useAddProfilePicture = () => {
  const [{fetching, error}, execute] = useMutation(AddProfilePicture_Mutation);
  return {
    error: !!error,
    fetching,
    addProfilePicture: execute,
  }
}

图片选择与上传逻辑

const pictureHandler = async () => {
    const options = {
      mediaType: 'photo' as MediaType,
      includeBase64: true,
      selectionLimit: 1,
    };
    
    const profilePicture = await launchImageLibrary(options);
    
    if (profilePicture.assets?.[0].fileSize && profilePicture.assets?.[0].fileSize > MAXFILESIZE) {
      showError(t('profileScreen.PictureSize'));
    }
    if (profilePicture.assets?.[0].uri && profilePicture.assets[0].fileName && profilePicture.assets[0].type) {
      const blob = new Blob([profilePicture.assets[0].base64 as BlobPart], {
        type: profilePicture.assets[0].type,
      });
      
      const file = new File([blob], profilePicture.assets[0].fileName, { type: `${profilePicture.assets[0].type}`});
      
      const {error} = await addProfilePicture(
          { userId: userId!, picture: file},
          { fetchOptions: { headers: { 'graphql-require-preflight': '' } } }
        );
      if (!error) {
        showSuccess(t('profileScreen.PictureSuccessAdded'));
        navigation.navigate('UserProfile');
      } else {
        console.log(error);
        showError(t('profileScreen.PictureErrorAdded'));
      }
    };
  };

最终解决办法

经过多方排查仍未定位GraphQL上传失败的根源,最终通过后端新增专门的图片上传接口,使用普通fetch POST请求完成上传:

// 替代方案示例代码
const uploadViaRest = async (uri: string, fileName: string, mimeType: string, userId: string) => {
  const formData = new FormData();
  formData.append('picture', {
    uri,
    name: fileName,
    type: mimeType
  } as any);
  formData.append('userId', userId);

  try {
    const response = await fetch('你的上传接口地址', {
      method: 'POST',
      body: formData,
      headers: {
        'Content-Type': 'multipart/form-data',
      },
    });
    const result = await response.json();
    if (response.ok) {
      showSuccess(t('profileScreen.PictureSuccessAdded'));
      navigation.navigate('UserProfile');
    } else {
      throw new Error(result.message);
    }
  } catch (err) {
    showError(t('profileScreen.PictureErrorAdded'));
    console.error(err);
  }
};

关键提示

  • GraphQL文件上传依赖服务器对multipart/form-data格式的GraphQL请求支持,比如Spring Boot后端需配置正确的文件上传解析器,同时urql客户端要启用对应文件上传插件(如urql-exchange-multipart-fetch)
  • React Native中的File/Blob对象与Web环境存在差异,手动构建的File对象可能无法被正确序列化,可尝试用react-native-document-picker获取原生文件引用替代launchImageLibrary
  • 若GraphQL上传方案持续受阻,REST接口上传是更直接的替代方案,无需在文件上传场景下过度依赖GraphQL

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 06:50:26