공부/코딩

와플 스튜디오 루키 과정 복습하기 - 3: AWS S3, image upload

고라닭 2025. 2. 22. 16:46

S3 is a file storage service provided by AWS.

 

This time, I configured a bucket to store images and created a page to upload images to the bucket.

 

To enhance security, I created an IAM account named 'rookie_reivew' instead of using the root account.

 

I plan to add additional policies only when necessary in the future. And I also issued an access key.

 

Using this IAM account, I created a bucket and configured its bucket policy to control access

 

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "1",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::rookie-review-test/*"
        },
        {
            "Sid": "2",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::559050210272:user/rookie_review"
            },
            "Action": [
                "s3:PutObject",
                "s3:DeleteObject"
            ],
            "Resource": "arn:aws:s3:::rookie-review-test/*"
        }
    ]
}

 

Edit the CORS settings. To make development more convenient, I temporarily set 'AllowedOrigins' in the CORS configuration to allow all origins.

 

[
    {
        "AllowedHeaders": [
            "*"
        ],
        "AllowedMethods": [
            "PUT",
            "POST",
            "GET"
        ],
        "AllowedOrigins": [
            "*"
        ],
        "ExposeHeaders": [
            "ETag"
        ]
    }
]

 

 

Let's write page for image upload.

 

'use client';

import { ChangeEvent, useState } from 'react';
import Image from 'next/image';

export const TestPage = () => {
  const [previewImg, setPreviewImg] = useState<File>();

  const saveHandler = async () => {
    if (!previewImg) return;

    const formData = new FormData();
    formData.append('image', previewImg);

    const result = await fetch('/api', {
      method: 'POST',
      body: formData,
    }).then((res) => res);

    if (result.status === 200) {
      alert('Successfully uploaded');
    }
  };

  const fileHandler = (e: ChangeEvent<HTMLInputElement>) => {
    if (e.target.files && e.target.files.length > 0) setPreviewImg(e.target.files[0]);
  };

  return (
    <div className="flex w-full h-full p-10 ">
      <form className="flex flex-col items-center w-full gap-10">
        <input type="file" onChange={(e) => fileHandler(e)} />
        {previewImg && <Image src={URL.createObjectURL(previewImg)} alt="Preview Image" width={400} height={400} />}
        <button type="button" onClick={() => saveHandler()} className="bg-gray-300 px-6 py-4 text-gray-900">
          save
        </button>
      </form>
    </div>
  );
};

 

 

app/api/route.ts

import { PutObjectCommand, S3Client } from '@aws-sdk/client-s3';
import { NextRequest, NextResponse } from 'next/server';

const Bucket = process.env.AMPLIFY_BUCKET as string;
const s3 = new S3Client({
  region: process.env.AWS_REGION as string,
  credentials: {
    accessKeyId: process.env.AWS_ACCESS_KEY_ID as string,
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY as string,
  },
});

export const POST = async (req: NextRequest) => {
  const form = await req.formData();
  const file = form.get('image') as File;

  if (!file || file.size === 0) {
    return new Response('No file uploaded', { status: 404 });
  }

  const buffer = await file.arrayBuffer();

  const fileParams = {
    Bucket: Bucket,
    Key: file.name,
    Body: new Uint8Array(buffer),
    ContentType: file.type,
  };

  await s3.send(new PutObjectCommand(fileParams));

  const url = `https://${fileParams.Bucket}.s3.amazonaws.com/${fileParams.Key}`;

  return NextResponse.json({ url }, { status: 200 });
};

 

 

 

It works successfully. :D

 

 

Server too!