↳ See other learning resources

AWS S3 Image Upload in Next.js

  1. Create your <input>:
const [image, setImage]=useState<File | undefined>();

<input
    type='file'
    accept="image/jpeg,image/png,image/webp,image/gif"
    onChange={e=>{
        setImage(e.target.files?.[0]);
    }}
/>
  1. Create an S3 bucket and IAM role in the AWS console. a. When creating the bucket, uncheck block all public access b. Set the bucket policy to
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Statement1",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::my_bucket_name/*"
        }
    ]
}

c. Set the bucket CORS policy to

[
    {
        "AllowedHeaders": [
            "*"
        ],
        "AllowedMethods": [
            "GET",
            "POST",
            "PUT"
        ],
        "AllowedOrigins": [
            "http://mywebsite.com",
            "https://mywebsite.com",
            // "http://localhost:3000" //include for when you are testing but remove it in production
        ],
        "ExposeHeaders": [],
        "MaxAgeSeconds": 3000
    }
]

d. Create a policy under IAM>Access Management>Policies e. Create a user in IAM and add that policy to the user f. Go to the IAM user's security credentials tab and create an access key


3. Add values from step 2 to your `.env`:

From S3 Bucket config

S3_BUCKET_NAME=my_bucket_name S3_REGION=us-east-1

From IAM User's Access Key

S3_ACCESS_KEY=... S3_SECRET_ACCESS_KEY=...


3. Run `npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner`

4. Implement `getSignedUrl`:
```tsx
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
import { getSignedUrl as s3GetSignedUrl } from '@aws-sdk/s3-request-presigner';
import crypto from 'crypto';
const generateRandomName=()=>crypto.randomBytes(16).toString('hex');
import prisma from '@/data/prisma/client';

const s3=new S3Client({
    region: process.env.S3_REGION!,
    credentials: {
        accessKeyId: process.env.S3_ACCESS_KEY!,
        secretAccessKey: process.env.S3_SECRET_ACCESS_KEY!,
    }
});

export async function getSignedUrl(id: string) {
    const key=await generateRandomName();

    const putObjectCommand=new PutObjectCommand({
        Bucket: process.env.S3_BUCKET_NAME,
        Key: key
    });

    const signedUrl=await s3GetSignedUrl(s3, putObjectCommand, { expiresIn: 60 });
    const imageUrl=signedUrl.split('?')[0];

    // Update your database to include the imageUrl
    await prisma.library.update({
        where: { id }
        data: { imageUrl }
    });

    return signedUrl;
}
  1. Create your onSubmit handler:
const signedUrl=await getSignedUrl(id); //id is used for updating the database entry

fetch(signedUrl, {
    method: 'PUT',
    body: image,
    headers: {
        'Content-Type': image.type,
    }
});