React Native:无法在“FormData”上执行“附加”:参数 2 不是“Blob”类型。在新的 ApolloError

Posted

技术标签:

【中文标题】React Native:无法在“FormData”上执行“附加”:参数 2 不是“Blob”类型。在新的 ApolloError【英文标题】:React Native: Failed to execute 'append' on 'FormData': parameter 2 is not of type 'Blob'. at new ApolloError 【发布时间】:2020-07-24 02:05:56 【问题描述】:

我正在尝试通过使用带有 createUploadLink() 的 Apollo 客户端将图像从我的 react 本机应用程序上传到 graphql。当我试图通过将 ReactNativeFile 作为变量传递来改变数据时,它会说

"network request failed: Failed to execute 'append' on 'FormData': parameter 2 is not of type 'Blob'. at new ApolloError ".

这是我正在尝试使用的突变

  mutation publishPost(
    $content: String!
    $LocationInput: LocationInput!
    $InputPostAttachment: [InputPostAttachment!]
  ) 
  publishPost(
    content: $content
    location: $LocationInput
    attachments: $InputPostAttachment
  )  
      content
     
  

InputPostAttachment 有类型

type InputPostAttachment 
   type: PostAttachmentType!
   file: Upload!

Apollo 客户端设置,我正在使用 apollo-upload-client

const httpLink = createUploadLink(
  uri: 'http://localhost:8000/graphql',
);

const authLink = setContext(async (headers: any) => 
  const token = await getToken();
  return 
    ...headers,
    headers: 
      authorization: token ? `Bearer $token` : null,
    ,
  ;
);

const link = authLink.concat(httpLink);

// create an inmemory cache instance for caching graphql data
const cache = new InMemoryCache();

// instantiate apollo client with apollo link instance and cache instance
export const client = new ApolloClient(
  link,
  cache,
);

文件上传功能,我正在使用 react-native-image-crop-picker 进行多图像选择

  const [image, setimage] = useState([]);

  const _pickImage = () => 
    ImagePicker.openPicker(
      includeBase64: true,
      multiple: true,
    ).then((images: any) => 
      let imageData: any = [];
      images.map((data: any) => 
        const file = new ReactNativeFile(
          uri: data.path,
          name: data.filename,
          type: data.mime,
        );
        imageData.push(
          type: 'IMAGE',
          file: file,
        );
      );
      setimage(imageData);
      console.log(images);
    );
  ;

  const handlePost = async () => 

    const InputPostAttachment: any = [...image];

    const LocationInput = 
      place: place,
      vicinity: vicinity,
      province: province,
    ;

    publishPost(variables: content, LocationInput, InputPostAttachment)
      .then((data) => 
        console.log(data);
        props.navigation.navigate('Home');
      )
      .catch((err) => 
        console.log('err happened');
        console.log(err);
      );
  ;

有人可以帮我解决这个问题吗?

【问题讨论】:

【参考方案1】:

我的 apollo-client 是使用 apollo-boost 配置的,而我使用 chrome 调试器拦截网络导致了这个问题。

更具体地说,我使用以下代码在 chrome 调试器中获取我的应用发送的网络请求

global.XMLHttpRequest =
  global.originalXMLHttpRequest || global.XMLHttpRequest;
global.FormData = global.originalFormData || global.FormData;

if (window.FETCH_SUPPORT) 
  window.FETCH_SUPPORT.blob = false;
 else 
  global.Blob = global.originalBlob || global.Blob;
  global.FileReader = global.originalFileReader || global.FileReader;

如果我们使用 chrome 调试器,apollo-upload-client 不会以多部分数据的形式发送数据。我们将面临网络问题。这个issue 有答案。或者我没有删除 apollo-boost 并且我的应用程序的某些部分正在使用它,这也是一个问题。

【讨论】:

【参考方案2】:

除了chrome调试器问题,expo web上也会出现这个错误。

对于任何在 expo web(或 react-native web)上上传图片的人,这里有一个可行的解决方案:

/** Load image from camera/roll. */
const result = await ImagePicker.launchImageLibraryAsync(
  mediaTypes: ImagePicker.MediaTypeOptions.All,
  allowsEditing: true,
  quality: 1,
);

if (result.cancelled) 
  return;


/** web platform: blob. */
const convertBase64ToBlob = async (base64) => 
  const response = await fetch(base64);
  const blob = await response.blob();
  return blob;
;

/** android/ios platform: ReactNativeFile.*/
const createReactNativeFile = (uri) => 
  const file = new ReactNativeFile(
    uri,
    type: mime.lookup(uri) || 'image',
    name: `file-$Date.now()`,
  );
  return file;
;

/** Use blob for web, ReactNativeFile otherwise. */
const file = Platform.OS === 'web'
  ? await convertBase64ToBlob(result.uri)
  : createReactNativeFile(result.uri);

/** Upload image with apollo. */
mutate( variables:  file  );

在网络平台上,ImagePicker 返回一个 base64 值而不是文件路径。如果平台是 Android 或 iOS,则不会发生此问题,因为 ImagePicker 返回一个文件路径,这是 apollo-upload-client 所期望的。

解决方案是检测 URI 是否为 base64(当平台为“web”时会发生这种情况)并将其转换为 blob。

【讨论】:

以上是关于React Native:无法在“FormData”上执行“附加”:参数 2 不是“Blob”类型。在新的 ApolloError的主要内容,如果未能解决你的问题,请参考以下文章

React-native 上传图片作为 FormData

将 react-native-signaturepad 输出转换为 formData?

FormData发送字符串值而不是react-native中的视频文件

关于React Native使用formData形式上传文件失败,请求失败报错:Net Error问题

react-native upoad imagepicker

React Native中通过axios上传图片