How to upload files to S3 with Expo


This is a practical example of how to upload files to S3 with Expo.

Previous requirements


Make sure you have Node.js and npm installed on your system. You can verify its installation by running the following commands in your terminal:

node  -v
npm  -v

If you don’t have Node.js and npm installed, you can download and install them from nodejs.org.

Create a new Expo project


npx  create-expo-app@latest  upload-files-s3-expo

Install dependencies


npm  i

Create a page to upload files

import { View, Text } from "react-native";
const UploadFiles = () => {
  return (
    <View>
      <Text>upload-files</Text>
    </View>
  );
};

export default UploadFiles;

Now create a logic to upload files install expo-document-picker or expo-image-picker

npx expo install expo-document-picker

Or

npx expo install expo-image-picker

Upload files from the device

—With expo-document-picker

const [image, setImage] = React.useState<DocumentPicker.DocumentPickerResult>();

const handlePickPhoto = async () => {
    const result = await DocumentPicker.getDocumentAsync({
      type: "image/png",
      copyToCacheDirectory: true,
    });
    if (result) {
      setImage(result);
    }
};

We created a function to open files, applied a filter to allow only PNG images to be uploaded, and stored the result in the image state.

Configure polyfills

Currently, uploading files from React Native with Expo presents several compatibility issues with blobs and random values. To address these problems, we installed the following packages:

  • react-native-url-polyfill
  • react-native-get-random-values
  • web-streams-polyfill@3.3.3
  • react-native-polyfill-globals
npx expo install react-native-url-polyfill react-native-get-random-values web-streams-polyfill@3.3.3 react-native-polyfill-globals

Now, in the _layout.tsx file, which serves as our entry point, we import the polyfills.

import 'react-native-url-polyfill/auto'
import 'react-native-get-random-values'
import 'web-streams-polyfill/ponyfill'
import 'react-native-polyfill-globals'

global.crypto.getRandomValues;
global.Buffer = Buffer;

global.ReadableStream = ReadableStream as any

With these imports, we now have compatibility with additional functionalities.

Configure AWS Client

import { S3 } from "@aws-sdk/client-s3";

export const s3Client = new S3({
  forcePathStyle: false,
  endpoint: //S3_ENDPOINT,
  region: //S3_REGION,
  credentials: {
    accessKeyId: //S3_ACCESS_KEY_ID,
    secretAccessKey: //S3_SECRET_ACCESS_KEY,
  },
});

Upload files to S3

We import our S3 client and configure our function to upload files.

import { s3Client } from "@/plugins/aws-s3";

const  UploadFiles  = () =>  {
const [image, setImage] = React.useState<DocumentPicker.DocumentPickerResult>();

---
//rest of the code
---

const handleUploadFile = async () => {
    if (image  && image.assets && image.assets.length > 0) {
      //Fetch the image from the device and convert it to a blob
      const blob_data = await fetch(image.assets[0].uri);
      const blob = await blob_data.blob();
      let URL = //Name of the file with the path  example: "logo/1234567890/1234567890/logo.png"
      try {
        const jsonUploadParams = {
          Bucket: //S3_BUCKET,
          Key: URL,
          Body: blob,
          ContentType: "image/png",
        };

        return await s3Client
          .putObject(jsonUploadParams)
          .then(() => {
            //Success message
            return URL;
          })
          .catch(() => {
            //Error message
            return "";
          });
      } catch {
        //Error message
        return "";
      }
    }
    return "";
  };

return ()

}

Conclusion

In conclusion, by addressing compatibility issues and configuring the necessary tools, we successfully set up a seamless file upload process in our React Native project. These steps ensure better functionality and integration with external services like S3.

Thank you for following along, and happy coding! 🚀