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

React Native中用RTK Query的GraphQLClient传文件报错:$file值无效

问题原因分析

报错Variable "$file" got invalid value, Upload value invalid的核心原因:

  • 当前使用的graphql-request库的GraphQLClient默认以application/json格式发送请求,不支持文件上传必需的multipart/form-data协议,也无法识别apollo-upload-client提供的ReactNativeFile类型变量。
  • 后端文件上传端点要求接收符合GraphQL Multipart Request规范的请求,但你的baseQuery没有将文件变量转换为对应的表单格式。
解决办法

方案一:改用Apollo Client作为GraphQL请求客户端(推荐,适配apollo-upload-client)

  1. 配置带上传支持的Apollo Client
import { ApolloClient, InMemoryCache } from '@apollo/client';
import { createUploadLink } from 'apollo-upload-client';
import { setContext } from '@apollo/client/link/context';

// 替换原httpLink为支持上传的createUploadLink
const uploadLink = createUploadLink({
  uri: process.env.REACT_APP_GRAPHQL_URL,
  credentials: 'include',
});

// 可添加认证逻辑(按需)
const authLink = setContext((_, { headers }) => {
  return {
    headers: {
      ...headers,
      'Access-Control-Allow-Credentials': 'true',
    },
  };
});

const client = new ApolloClient({
  link: authLink.concat(uploadLink),
  cache: new InMemoryCache(),
});
  1. 调整RTK Query的baseQuery为Apollo Client适配版本
import { createApi } from '@reduxjs/toolkit/query/react';
import { graphqlBaseQuery } from '@rtk-query/apollo-base-query';

export const api = createApi({
  baseQuery: graphqlBaseQuery({ client }), // 使用配置好的Apollo Client
  endpoints: () => ({}),
});
  1. 原有mutation调用代码无需修改,Apollo Client会自动处理ReactNativeFile的转换和multipart请求发送

方案二:修改现有graphql-request的baseQuery,手动处理文件上传

如果不想切换客户端,可手动改造baseQuery以支持文件上传:

  1. 安装依赖(React Native环境)
yarn add react-native-form-data
# 或
npm install react-native-form-data
  1. 修改rawBaseQueryGQL代码:
import FormData from 'react-native-form-data';
import { ReactNativeFile } from 'apollo-upload-client';
import { print } from 'graphql';

export const rawBaseQueryGQL: BaseQueryFn<
  IGQLBaseQuery,
  unknown,
  Partial<ClientError & { errorCode: number }>
> = async ({ document, variables }, api, extraOptions) => {
  // 检测变量中是否包含ReactNativeFile实例
  const hasUpload = Object.values(variables || {}).some(v => v instanceof ReactNativeFile);

  if (hasUpload) {
    // 构建multipart/form-data请求
    const formData = new FormData();
    
    // 添加GraphQL查询和变量
    formData.append('operations', JSON.stringify({
      query: print(document),
      variables: variables,
    }));

    // 构建文件映射关系
    const fileMap: Record<string, string[]> = {};
    let fileIndex = 0;
    Object.entries(variables || {}).forEach(([key, value]) => {
      if (value instanceof ReactNativeFile) {
        const fileKey = `file${fileIndex}`;
        fileMap[fileKey] = [`variables.${key}`];
        formData.append(fileKey, value as any);
        fileIndex++;
      }
    });
    formData.append('map', JSON.stringify(fileMap));

    // 发送请求
    try {
      const response = await fetch(process.env.REACT_APP_GRAPHQL_URL, {
        method: 'POST',
        credentials: 'include',
        body: formData,
        headers: {
          'Access-Control-Allow-Credentials': 'true',
          // 无需手动设置Content-Type,FormData会自动添加boundary
        },
      });

      const result = await response.json();
      if (result.errors) {
        const { errorMessage = "", errorCode = 500 } = result.errors[0]?.extensions || {};
        return {
          error: {
            name: 'GraphQLError',
            message: errorMessage,
            errorCode,
          },
        };
      }
      return { data: result.data };
    } catch (err) {
      return { error: err as any };
    }
  }

  // 非文件上传请求,走原有逻辑
  const client = new GraphQLClient(
    process.env.REACT_APP_GRAPHQL_URL,
    { credentials: "include" }
  );

  client.setHeader("Accept", "application/json");
  client.setHeader("Content-Type", "application/json");
  client.setHeader("Access-Control-Allow-Credentials", "true");

  const query = graphqlRequestBaseQuery<
    Partial<ClientError & { errorCode: number }>
  >({
    client,
    customErrors: ({ name, stack, response }) => {
      const { errorMessage = "", errorCode = 500 } = response?.errors?.length
        ? response?.errors[0]?.extensions
        : {};

      return { name, message: errorMessage, errorCode, stack };
    },
  });

  return query({ document, variables }, api, extraOptions);
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 17:03:19