Three computer monitors displaying blue binary code with a glowing digital sphere and city skyline in the background.
Three computer monitors displaying blue binary code with a glowing digital sphere and city skyline in the background.

React & Expo: A journey of uploading raw image data to S3

Explore how to upload raw image data to Amazon S3 in a React Native Expo app. This guide walks through handling image selection, converting data for upload, and securely sending files to S3, with practical code examples.

Our journey beings when using the React ImagePicker, where the user of our application can select the image and then we receive a result that contains the URI of our associated image.

const pickImage = async () => {
  const result = await ImagePicker.launchImageLibraryAsync({
    mediaTypes: ImagePicker.MediaTypeOptions.All,
    allowsEditing: true,
    aspect: [4, 3]

We need to take this URI and ultimately upload it to Amazon S3 through a signed URL. Unfortunately, we cannot just simply use the URI like we would with FormData like we would if we were uploading to an API because this is a PUT request with the data contained directly.

Ok, so let us get the contents of the image and all we have is the URI. Why can I not get the image as something more useful? Let’s break out the FileReader to get this as an ArrayBuffer. Wait…! You mean to tell me that Expo and React do not contain readAsArrayBuffer?!

const pickImage = async () => {
  const result = await ImagePicker.launchImageLibraryAsync({
    mediaTypes: ImagePicker.MediaTypeOptions.All,
    allowsEditing: true,
    aspect: [4, 3]

Everything is Falling Apart

Maybe there is another path, so we start searching for Upload Image to S3 from Expo (or React). You will run into examples where people use a custom backend (don’t do this). Back to looking for a way to read the image as an array buffer. Maybe there is a readAsArrayBuffer polyfill or an NPM module that will handle this situation... If you are using React, you will likely have found that you can use RNFetchBlob which will work for you. Although, those of you on Expo have struck out. Maybe it’s time to eject the app for this fundamental requirement… However, we cannot believe there is not a different way.

The Easy Way Forward

Fortunately, there is an easy solution. You do not need to require a new NPM module, you don’t need readAsArrayBuffer, all of the tools are there for you and quite literally, it was far too easy and obvious. What you might not know is that you can set the body of a fetch request to a File reference. We can actually create one of these directly from a blob.

const pickImage = async () => {
  const result = await ImagePicker.launchImageLibraryAsync({
    mediaTypes: ImagePicker.MediaTypeOptions.All,
    allowsEditing: true,
    aspect: [4, 3],
    quality: 1,
  });
  if (!result.cancelled) {
    const imagePath = result.uri;
    const imageExt = result.uri.split('.').pop();
    const imageMime = `image/${imageExt}`;
    let picture = await fetch(imagePath);
    picture = await picture.blob();
    const imageData = new File([picture]

Conclusion

Sometimes the answers are right in front of us. In this case, we knew we likely needed to pass an array buffer. Unfortunately, instead of looking for another solution we dug in and went too deep. When this happens, generally it is time to look for a different way and a different way proved to be the better, more condense and fool proof solution.

hello@thesparklaboratory.com

hello@thesparklaboratory.com

The best ideas don't wait. Let's talk & make it happen.

Contact Us

Contact Us

Contact Us

© Spark Labs 2025. All rights reserved.