React-native image uploading and starlette backend not behaving well

Hi everyone,
I have known Starlette recently and love it so far. Our love-story was perfect until 3 days ago when I hit a very strange/tough problem.

My build:

[Front-end]

I am using a react-native app code here with the react-native-image-picker library to upload an image and send it to my back-end.
The image picker doesn’t create a Javascript File object, so I need to use a trick to pass the image in a formdata object anyway and send it via POST request.

const data = new FormData();
data.append("file", {
     name: photo.fileName,
    type: photo.type,
    uri: Platform.OS === "android" ? photo.uri : photo.uri.replace("file://", "")
});

fetch("http://localhost:5000/analyze", {
      method: "POST",
      body: data
    })
      .then(response => response.json())
      .then(response => {
        console.log("upload succes", response);
        alert("Upload success!");
        this.setState({ photo: null });
      })
      .catch(error => {
        console.log("upload error", error);
        alert("Upload failed!");
      });

[Back-end]

I am using Starlette (yeay!). Here is my code
Basically, I do the following to get my image:

@app.route('/analyze', methods=['POST'])
    async def analyze(request):
        img_data = await request.form()
        # import pdb; pdb.set_trace()
        tmp_img = img_data['file']
        # print('type', type(tmp_img))
        img_bytes = await (tmp_img.read())
        img = open_image(BytesIO(img_bytes))

open_image is a FastAI function similar to open() on a Image.

My problem

When I do a standard POST request with Form Data of type File attaching an image with the Postman software, my backend works like a charm. (it also works like a charm from a html form with file )
Especially, the type of data of the above code is the following:

img_data => starlette.datastructures.FormData
tmp_img => starlette.datastructures.UploadFile

When I send it from my React-native app. I receive the POST in the Backend but here is the type I get:

img_data => starlette.datastructures.FormData
tmp_img => str

And, the error, obviously: cannot do .read() on a string
The string looks like a string of bytes that might represent my image.

My unsucessful attempts

  1. Creating a Blob with the pic in the FE, add it to the FormData and send it to BE
    => same result as above

  2. Creating a File with the pic in the FE, add it to the FormData and send it to BE
    => could not create the File because File is a react component and I couldn’t find the right arguments to pass to create it

  3. Get a base64 encode of the pic in FE, send it to BE and decode it
    => The decode couldn’t recognize the file as an image (maybe due to my noobness in PY)

  4. Convert tmp_img from str to an image
    => Couldn’t work it out (maybe due to my noobness in PY)

As you can see I tried a lot of things with no luck :frowning: Would anyone have an idea to get me out of this mayhem?
Thanks a lot for you help and have a great week!

Yeah, this is definitely an RN usage thing rather than Starlette. I remember dealing with this as well about a year ago, but in that situation it solved itself while transitioning to GraphQL. Not entirely sure what it’s doing under the hood, but we needed this Apollo Link library in order to shoehorn blob uploads into multipart GraphQL. This had the side effect of also just… fixing this for us.

Sorry I can’t be too helpful specifically, as I do remember beating my head into the wall for a little bit over this too. But you might want to check that library (or other things that work) to see how they’re creating the request. And the thing is, while the FormData code you posted is the standard boilerplate I always see for this, it clearly does not work the way it should and form requests the same way, or else it would work.

You should probably look at the raw HTTP requests being sent from Postman vs. RN. I imagine once you figure out exactly how the request structures differ, you’ll be on your way. Share this back here as well once you figure it out, if you’d be so kind :slight_smile: