Axios in React Native - Cannot POST a Blob or File
I'm trying to post the raw data of a picture, using Axios, after taking it with react-native-image-picker
.
I successfully generated a blob using this piece of code:
const file = await fetch(response.uri);
const theBlob = await file.blob();
If I inspect the metadata of the blob it's all right: MIME type, size, and so on.
However, when I try to POST it using Axios, using:
axios({
method: "POST",
url: "https://my-api-endpoint-api.example.org",
data: theBlob,
});
what I receive on the API side is this strange JSON payload:
{"_data":{"lastModified":0,"name":"rn_image_picker_lib_temp_77cb727c-5056-4cb9-8de1-dc5e13c673ec.jpg","size":1635688,"offset":0,"type":"image/jpeg","blobId":"83367ee6-fa11-4ae1-a1df-bf1fdf1d1f57","__collector":{}}}
The same code is working fine on React, and I have the same behavior trying with a File
object instead of a Blob
one.
I see in other answers that I could use something else than Axios, like RNFetchBlob.fetch()
, but since I'm using shared functions between the React website and the React Native app, I'd really prefer an approach that allows me to use Axios and Blob
s.
Is there some way to work around it?
Updated answer
As pointed out by @T.J.Crowder in the comments, there is a cleaner approach that works around the issue of the React Native host environment, without touching anything else on the code.
It's enough to add this on the index.js
file, before everything else:
Blob.prototype[Symbol.toStringTag] = 'Blob'
File.prototype[Symbol.toStringTag] = 'File'
I leave my original answer here under since it's a working alternative if one doesn't want to mess up with the prototype.
Original answer
The described behavior happens because the host environment of React Native does not handle the Blob
type nicely: it will actually become just an object.
In fact, if you try to render toString.call(new Blob())
in a component, you'll see [object Blob]
in a browser, but [object Object]
in React Native.
The matter is that the default transformRequest
implementation of Axios will use exactly this method (toString.call
) to check if you're passing a Blob
or some generic object to it. If it sees you're passing a generic object, it applies a JSON.stringify
to it, producing the strange JSON you're seeing POSTed.
This happens exactly here: https://github.com/axios/axios/blob/e9965bfafc82d8b42765705061b9ebe2d5532493/lib/defaults.js#L61
Actually, what happens here is that utils.isBlob(data)
at line 48 returns false, since it really just applies toString on the value and checks if it is [object Blob]
, which as described above is not the case.
The fastest workaround I see here, since you're sure you're passing a Blob
, is just to override transformRequest
with a function that just returns the data as it is, like this:
axios({
method: "POST",
url: "https://my-api-endpoint-api.example.org",
data: theBlob,
transformRequest: (d) => d,
});
This will just make the request work.