Image Uploads to AWS S3 with Next.js: A Step-by-Step Guide
📖 3 min read
If you have been developing a serverless web application like the one I created, and are seeking a method to upload files to the cloud, such as AWS S3, then this guide is for you. In this tutorial, we'll delve into integrating AWS S3 into a Next.js application to enable smooth image uploads.
Preparation: Setting Up AWS S3
Before we start, please follow the steps below to set up your AWS S3 account:
Step 1: Create an S3 bucket
Step 2: Create a new IAM User and attach policies related to S3 operations
Step 3: Save the access key and secret key for the IAM User to .env.local file
Step 4: Configure CORS to enable uploads from the browser
For more details, please read this article from Vercel
Building Client Component
We now create a client component in Next.js to accept image uploads from users.
'use client'
import React, { useState } from 'react';
function ImageUploadForm() {
const [image, setImage] = useState(null);
const handleImageUpload = (e) => setImage(e.target.files[0]);
const handleSubmit = async (e) => {
e.preventDefault();
if (!image) {
console.error('No image selected');
return;
}
try {
const formData = new FormData();
formData.append('image', newImage);
const response = await fetch('/api/upload', {
method: 'POST',
body: formData
});
if (!response.ok) {
throw new Error('Failed to upload image');
}
console.log('Image uploaded successfully');
} catch (error) {
console.error('Error uploading image:', error.message);
}
};
return (
<form onSubmit={handleSubmit}>
<label htmlFor="imageInput">Choose File:</label>
<input
type="file"
id="imageInput"
name="image"
accept="image/*"
onChange={(e) => handleImageUpload(e)}
/>
<input type="submit" value="Upload" />
</form>
);
}
export default ImageUploadForm;
We define the ImageUploadForm component with some key parts:
The useState hook create state for the selected image file, which will hold the selected file object.
The handleImageUpload function is triggered when a user selects an image file, updating the image state with the selected file.
The handleSubmit function is triggered when the form is submitted. It checks if an image has been selected, constructs a FormData object containing the image data, sends a POST request to the server endpoint (/api/upload), and handles success or error responses accordingly.
Uploading Images to AWS S3
Once we received the image file from the frontend, we need to create an API route that consumes the data with AWS SDK and upload the image to the S3 bucket.
Install AWS SDK and configure AWS Credentials
In the Next.js project directory, we run:
npm install @aws-sdk/client-s3
to install the AWS SDK for JavaScript. Also make sure you have the AWS Credentials stored in the .env.local file:
AWS_REGION=YOUR_REGION AWS_ACCESS_KEY_ID=YOUR_AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY=YOUR_AWS_SECRET_ACCESS_KEY
Configure S3 Client and send POST request
Let's create a file app/api/upload/route.js and initialize an S3 client using the AWS SDK:
import { NextResponse } from 'next/server'; import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3'; const s3Client = new S3Client({ region: process.env.AWS_REGION, credentials: { accessKeyId: process.env.AWS_ACCESS_KEY_ID, secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, }, });
And create an asynchronous function that responsible for uploading a file to an AWS S3 bucket:
async function uploadFileToS3(file, fileName) {
const fileBuffer = file;
const params = {
Bucket: process.env.AWS_S3_BUCKET_NAME,
Key: `${fileName}`,
Body: fileBuffer,
ContentType: 'image/jpg',
};
const command = new PutObjectCommand(params);
await s3Client.send(command);
return fileName;
}
Finally, we add a Next.js API Route for POST request:
export async function POST(request) {
try {
const formData = await request.formData();
const file = formData.get('image');
if (!file) {
return NextResponse.json(
{ error: 'Image is required.' },
{ status: 400 },
);
}
const buffer = Buffer.from(await file.arrayBuffer());
const fileName = await uploadFileToS3(buffer, file.name);
return NextResponse.json({ success: true, fileName });
} catch (error) {
return NextResponse.json({ error });
}
}
We extracts the file data from the incoming HTTP request using the formData method and retrieves the value of the 'image' field from the form data.
If the 'image' field is missing (i.e., no file was uploaded), we returns a response with a 400 status code and a JSON object containing an error message indicating that the image is required.
If there is file, we convert it from ArrayBuffer format to a Buffer object. Using the uploadFileToS3 created earlier, we pass the file buffer and the file name. If the operation succeeds, we expect to receive the uploaded file's name, which should match the name of the object created in the AWS S3 bucket.
The name of image can then be further used to retrieve a presigned URL from AWS S3.
Conclusion
The steps and codes described above are exactly the same as the ones I used for both this portfolio website and this project. Give it a try and let me know what you think!