Search code examples
javascriptreact-nativefile-uploadreact-native-iosrn-fetch-blob

React-native RNFetchBlob: Axios Post request sends string instead of binary data


I have been implementing upload file functionality into React-native recently and came across an error where I need to post binary file data into Request body.

First of all, I have checked how Postman works and I tested this API into postman where I send Authorization and uid into request headers and body will be Form-Data with document = binary file.

See below is document key where we are able to select file object in Postman.

Postman request body representation. Document is of type File.

Now this Postman request succeeds this way and if I check relevant code for NodeJs-Axios, it's just as added here.

This code is from Postman intended just for explanation.

var axios = require('axios');
var FormData = require('form-data');
var fs = require('fs');
var data = new FormData();
data.append('document', fs.createReadStream('LOCAL_PATH_TO_FILE/Screenshot 2021-01-28 at 12.26.32 PM.png'));

var config = {
  method: 'post',
  url: 'API_BASE/addDocument/',
  headers: { 
    'Authorization': 'Firebase_ID_Token', 
    'uid': 'Firebase_User_ID', 
    ...data.getHeaders()
  },
  data : data
};

axios(config)
.then(function (response) {
  console.log(JSON.stringify(response.data));
})
.catch(function (error) {
  console.log(error);
});

As we are not able to use the same function added above for React-native and I have used react-native-document-picker, when I pick any file, the object I get is like,

{
 
    "size":1173,
    "fileCopyUri":"file:///Users/Local_File_Path.plist",
    "name":"File_Name.plist",
    "uri":"file:///Users/Local_File_Path.plist"
}

I need to upload this file (e.g. plist) via API call to /adDocument endpoint where this endpoint expects request body as document: (binary data).

So, here is my main function for addDocument API call which needs to be corrected to send proper data to API.

import RNFetchBlob from 'rn-fetch-blob';
var RNFS = require('react-native-fs');

async uploadFileData (fileData) {
  try {
    console.log("***** uploadFileData parameters =>>>>> ", fileData);

    const mainPath = fileData.uri.replace("file://", "");
    let readStreamData = await RNFetchBlob.fs.readStream(mainPath, 'base64');
    let readFileData = await RNFS.readFile(mainPath);
    let wrapData = RNFetchBlob.wrap(mainPath);
    
    let data = new FormData();
    data.append('document', readStreamData, {'name': fileData.name});
    //data.append('document', new Blob([readFileData]), {'name': fileData.name});
    //data.append('document', readFileData, {'name': fileData.name});
    
    const apiHeader = {
        'Authorization': 'Google_ID_Token',
        'uid': 'Google_User_ID',
        'Content-Type': 'multipart/form-data',
        'Cache-Control' : 'no-store'
    };

    const res = await axios({ 
      method: 'POST',
      headers: apiHeader,
      data: data,
      baseURL: 'BASE_URL',
      url: '/addDocument/'
    });
  
    console.log("document/addDocument =>>>>> ", res);
    return res;
  } catch (e) {
    console.log("xxxxxxx document/addDocument Error =>>>>> ", e);
    return {  error: e  };
  }
}

I always receive failure with this function using any of the above mentioned methods, readStreamData, readFileData or wrapData. The reason is, I am never able to pass binary data into document.

What I see in request body is like,

enter image description here

Here, [object Object] is a string and not an object and so, my API call throws me error stating, data missing. It always passes string event I use 'Blob()'.

So, I want to know what I am doing wrong here so my request body is not passing binary data. Please send me any pointers on the topic so I can reach to the solution.


Solution

  • That was really issue with React-native debugger and not the actual implementation issue. As mentioned here => https://github.com/jhen0409/react-native-debugger/blob/master/docs/network-inspect-of-chrome-devtools.md, the debugger messes around with FormData and whatever being passed into FormData turns as [object Object] string due to uri property.

    I had turned off debugging and tried on both device and simulator, where following code worked as expected. No need to explicitly convert file into blob.

    async simpleFileUpload (fileData) {
      try {
        console.log("***** uploadFileData parameters =>>>>> ", fileData);
    
        const mainPath = fileData.uri.replace("file://", "");
        const file = {
          uri: mainPath,
          type: fileData.type,
          name: fileData.name || fileData.uri.substr(fileData.uri.lastIndexOf('/') + 1)
        };
    
        let formData = new FormData();
        formData.append('document', file);
        
        const res = await axios({ 
          method: 'POST',
          headers: this.apiHeaderUpload(),
          data: formData,
          baseURL: 'http://103.53.72.244:9091/api',
          url: '/document/addDocument/'
        });
    
        console.log("document/addDocument =>>>>> ", res);
        //alert('uploadFileData::Upload Successful >>> ' + JSON.stringify(res));
        return res;
      } catch (e) {
        console.log("xxxxxxx document/addDocument Error =>>>>> ", e);
        //alert('uploadFileData::Upload Error >>> ' + JSON.stringify(e));
        return {  error: e  };
      }
    }
    

    As simple as that :).