An example of uploading images from iOS, Android or web to an AWS S3 bucket using Expo Router and EAS Hosting.
To upload content to S3 securely, we create an API route which will generate a signed URL that allows the client to upload a file to a specific bucket for a fixed duration. This API route is a server-side function which will be deployed to EAS Hosting, meaning it is safe to use sensitive environment variables such as your S3 bucket credentials.
s3-full.mp4
Create a new project with this example:
npx create-expo-app --example with-s3You may use credentials for an existing S3 bucket, or create a new one.
First, make a copy of .env.example in this codebase and rename it to .env.local. This file will be gitignored and should never be checked into source control.
The instructions below explain how to create a new bucket, but to use an existing one, fill in the environment variables in .env.local.
Now let's create an S3 bucket. Log in to your AWS account and create a new bucket. Ensure you uncheck "Block all public access".
Open your bucket and under the "Permissions" tab, under "Bucket policy", edit it and add the following (ensure you replace YOUR-BUCKET-NAME with your bucket name):
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::YOUR-BUCKET-NAME/*"
}
]
}Open your .env.local and add your AWS_REGION (e.g. us-east-1) and the AWS_BUCKET_NAME (e.g. my-bucket).
Now we need to create an IAM user that has permission to upload to your S3 buckets.
Log in to your AWS account and create a new IAM user. On your newly created IAM user, click "Create access key". You'll only see these values once: add them to your .env.local under AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY.
To support web uploads and to be able to view the uploaded image, you also need to add the CORS policy. In your bucket, open Permissions -> Cross-origin resource sharing (CORS) and add the following:
[
{
"AllowedHeaders": ["*"],
"AllowedMethods": ["GET", "PUT", "POST", "DELETE"],
"AllowedOrigins": ["*"],
"ExposeHeaders": []
}
]Warning
The above setup for S3 creates a highly permissive policy for testing purposes. For production use, ensure you adjust this accordingly to your project requirements.
After completing the above, you should be able to upload images locally on iOS, Android, and Web. The API route is running locally at http://localhost:8081. When you've confirmed it is working as expected, see the next section about deploying your API routes.
Export the web project:
npx expo export --platform webNote
If you are looking to implement upload for your iOS and Android projects only, add --no-ssg to your export command. This will create a deployable bundle with only your API routes, without the web UI.
(Optional) Upload the environment variables to EAS environment variables
npx eas-cli env:push --environment productionNote
The environment variables will default to "plain text" visibility on the EAS dashboard. You may change the visibility on the UI, but only use plain text or sensitive (secret environment variables cannot be used in local deployments).
Deploy the project:
npx eas-cli deploy --environment production --prodNote
The --environment production will use environment variables uploaded earlier. Omitting this flag will use environment variables in your local .env or .env.local.
The --prod flag will promote the deployment to production, so it will be accessible under https://your-chosen-name.expo.app.
To test your upload against the deployed API, change the API_URL constant in app/index.tsx to your deployed URL.