Next.js Image Upload to Amazon S3 2023
Code drop for uploading images via a Next.js API route to Amazon AWS S3 using React, Next.js and Amazon S3
This tutorial demonstrates how to use a pre-signed URL to upload an image to Amazon/AWS S3 and store the link in a Prisma database.
Table of contents
Backend API Route code
import { NextApiRequest, NextApiResponse } from "next";
import prisma from "../../client";
import { getSession } from "next-auth/client";
import aws from "aws-sdk";
export default async function (req: NextApiRequest, res: NextApiResponse) {
const session = await getSession({ req });
// Update AWS configuration with the provided credentials
aws.config.update({
region: "eu-west-2",
accessKeyId: process.env.AWS_ACCESS_KEY,
secretAccessKey: process.env.AWS_SECRET,
});
const s3Bucket = process.env.AWS_BUCKET;
// Create a new instance of S3
const s3 = new aws.S3();
const fileName = req.body.fileName;
const fileType = req.body.fileType;
const s3Params = {
Bucket: s3Bucket,
Key: `businesslogos/${fileName}`,
ContentType: fileType,
ACL: "public-read",
};
try {
// Get a signed URL from S3 for uploading an object
s3.getSignedUrl("putObject", s3Params, async (err, data) => {
if (err) {
return res.json({ success: false, error: err });
}
const returnData = {
signedRequest: data,
url: `https://${s3Bucket}.s3.amazonaws.com/businesslogos/${fileName}`,
};
const imageUrl = await prisma.user.update({
where: {
email: session.user.email,
},
data: {
business: {
update: {
businessLogo: returnData.url,
},
},
},
});
return res.status(200).json(returnData);
});
} catch (err) {
return res.status(500).json(err);
}
}
const handleUpload = (ev) => {
let file = uploadInput.current.files[0];
// Split the filename to get the name and type
let fileParts = uploadInput.current.files[0].name.split(".");
let fileName = fileParts[0];
let fileType = fileParts[1];
axios
.post("/api/awsimageupload", {
fileName: fileName,
fileType: fileType,
})
.then((res) => {
const signedRequest = res.data.signedRequest;
const url = res.data.url;
setUploadState({
...uploadState,
url,
});
var options = {
headers: {
"Content-Type": fileType,
},
};
axios
.put(signedRequest, file, options)
.then((_) => {
setUploadState({ ...uploadState, success: true });
mutate();
})
.catch((_) => {
toast("error", "We could not upload your image");
});
})
.catch((error) => {
toast("error", "We could not upload your image");
});
};
Front end component
Below is the code for the front-end component. It includes an input for selecting a file, a button to trigger the upload, and a function to handle the upload process.
import { useRef, useState } from "react";
import axios from "axios";
import { toast } from "react-toastify";
const ImageUpload = () => {
const uploadInput = useRef(null);
const [uploadState, setUploadState] = useState({});
const handleUpload = (ev) => {
let file = uploadInput.current.files[0];
// Split the filename to get the name and type
let fileParts = uploadInput.current.files[0].name.split(".");
let fileName = fileParts[0];
let fileType = fileParts[1];
// Post the file information to the server to obtain a signed URL
axios
.post("/api/awsimageupload", {
fileName: fileName,
fileType: fileType,
})
.then((res) => {
const signedRequest = res.data.signedRequest;
const url = res.data.url;
setUploadState({
...uploadState,
url,
});
// Perform the actual upload using the signed URL
const options = {
headers: {
"Content-Type": fileType,
},
};
axios
.put(signedRequest, file, options)
.then((_) => {
setUploadState({ ...uploadState, success: true });
toast("success", "Image uploaded successfully");
})
.catch((_) => {
toast("error", "We could not upload your image");
});
})
.catch((error) => {
toast("error", "We could not upload your image");
});
};
return (
<div>
<input
type="file"
ref={uploadInput}
onChange={handleUpload}
style={{ display: "none" }}
/>
<button onClick={() => uploadInput.current.click()}>Upload Image</button>
{uploadState.success && <p>Image uploaded successfully!</p>}
</div>
);
};
export default ImageUpload;
Now, your Next.js application can upload images to Amazon S3 and store the link in a Prisma database. The backend API route handles generating a pre-signed URL for uploading to S3 and updating the Prisma database, while the front-end component handles user interactions and image uploads.