Search code examples
androidreact-nativefile-uploadaxiosreact-native-image-picker

react-native-image-picker+axios uploads fail on Android


I have an app where I use react-native-image-picker to take and select photos. Then I use axios and FormData to upload the selected files to my backend.

This works fine on iOS, where I can just take the URL from the image picker, and add it to my FormData and POST it to the server.

But on Android, the request stalls when I use the same code. It can post when I leave out the file and have only text-fields in my multipart form-data. But when I refer to file, the request never ends.

Adding CORS support to the webserver does not help. Adding the domain of the server to network_security_config.xml also does not help. As said, I can GET and POST to the endpoint as long as I don't add any files to the FormData/

I can see that my URIs that are returned from the image picker is in the style of content://my.app.id.imagepickerprovider/cacheDir/rn_image_picker_lib_temp_76d709ca-3d09-4b20-8ce4-bd8280180141.jpg on Android, which seems to be an invalid path.

Then I researched on how to convert that content:// to a valid file system path, and tried various solutions, using stat() in RNFetchBlob and RNFS, none of them worked. I also tried making my own custom module, which did not work, and ended up installing the react-native-get-real-path module which seems to convert the path properly. I now have something like /data/user/0/my.app.id/cache/rn_image_picker_lib_temp_638f24e9-0974-4fef-b6f0-e0a3873a14e6.jpg

So now, when i make my FormData, I'm adding the file with an URI like file:///data/user/0/my.app.id/cache/.....jpg

But this still makes my HTTP request hang, just like if I just pass the content:// URI.

I have also tried using the base64 output from react-native-image-picker and then set the data:... URI as the uri in the file form field, this works on iOS (but is incredibly slow), and does not work on Android.

I must have missed something - any tips?

In my component:


const imagePickerCallback = useCallback((response: ImagePickerResponse) => {
    setCurrentPhoto(response)
  }, [])

const handleUpload = useCallback(async () => {
    const { uri, base64, fileName, fileSize, type } = currentPhoto
    fileUploadStore.add(uri, fileName, type, fileSize) 
  }, [currentPhoto, fileUploadStore])


My FileUploadStore calls my API which tries to work with the URI and call axios. The results of the different solutions are in the comments (actualUri).

  async uploadFile(
    uri: string,
    name: string,
    type: string,
    onUploadProgress?: (progressEvent: ProgressEvent) => void,
    cancelToken?: CancelToken
  ): Promise<string> {
    const formData = new FormData()
    // Gives `NETWORK_ERROR` on android - file://content://dk.bosj.app.debug.imagepickerprovider/cacheDir/
    const actualUri = Platform.select({ ios: uri, android: `file://${uri}` })
    // Gives `NETWORK_ERROR` on android - /data/user/0/dk.bosj.app.debug/cache/rn_image_pi
    const actualUri = Platform.select({ ios: uri, android: await RNGRP.getRealPathFromURI(uri) })
    // Hangs forever on android  - content://dk.bosj.app.debug.imagepickerprovider/cacheDir/
    const actualUri = Platform.select({ ios: uri, android: uri })
    // Hangs forever on android  - file:///data/user/0/dk.bosj.app.debug/cache/rn_image_pi
    const actualUri = Platform.select({ ios: uri, android: `file://${await RNGRP.getRealPathFromURI(uri)}` })

    formData.append('file', {
      uri: actualUri,
      name,
      type,
    })
    console.log(JSON.stringify(formData))
    formData.append('stuff', 'AAAGNAAAA')
    const response = await this.api.post<{ id: string }, null>(BosjEndpoint.FILES, formData, {
      onUploadProgress,
      cancelToken,
    })
    if (!response.ok) {
      throw new Error(response.problem)
    }
    if (!response.data || !response.data.id) {
      throw new Error('DATA_ERROR')
    }
    return response.data.id
  }

Solution

  • OK the problem was very far away from the solutions I tried.

    Updating the flipper SDK version on Android did the trick, now file uploads work!

    I updated from 0.37.0 to 0.75.1

    https://github.com/facebook/react-native/issues/28551#issuecomment-624347067