🏞️
とりあえずNext.jsでS3を使って画像アップロード機能をつくってみる
S3をあまり触ったことがない方に向けて、何でもいいから最速でNext.jsからS3に画像をアップロードして、その画像を表示させるところまでやってみようという感じです。
下に表示される画像はS3上にアップロードされたファイルのリンクで表示させています。
適当にNext.js プロジェクトを作成
(※どちらでもいいですが今回はAppRouterにします)
npx create-next-app s3-upload-app --typescript
AWS側でS3の設定を行う
名前はなんでもいいのでS3でバケットを作成します。
またアップした画像ですが、アプリ側で表示できるようにしたいため、公開設定をパブリックに変更しておきます。
バケット名をクリックして「アクセス許可」→「バケットポリシー」に以下を記述します。
(Resourceの部分は作成したバケット名に変更してください)
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::your_bucket_name/*"
}
]
}
以上でS3の設定は終わりです。
S3にアクセスするためにIAMが必要なのでこちらの設定もしていきます。
アプリからS3にアクセスするためにIAMの設定を行う
IAMで適当なユーザーを作成してポリシーを設定します。
「ユーザーの作成」から「ポリシーを直接アタッチする」を選択して「AmazonS3FullAccess」を選択します。(今回はフルアクセスでやっていますが必要に応じて変更してください)
作成したユーザーをクリックして「アクセスキーを作成する」を選択します。
++コマンドラインインターフェイス (CLI)**を選択してアクセスキーを作成します。
この時に取得できるアクセスキーとシークレットアクセスキーをメモしておきます。
設定した内容を.env.localに認証情報を保存
.env.local
AWS_ACCESS_KEY_ID=your_access_key
AWS_SECRET_ACCESS_KEY=your_secret_key
AWS_REGION=your_region
S3_BUCKET_NAME=your_bucket_name
Head | Head |
---|---|
AWS_ACCESS_KEY_ID | IAMで作成したユーザーのアクセスキー |
AWS_SECRET_ACCESS_KEY | IAMで作成したユーザーのシークレットアクセスキー |
AWS_REGION | S3のAWSリージョン(例: 東京の場合はap-northeast-1) |
S3_BUCKET_NAME | S3で作成したバケット名 |
ページとAPIを作成
作成したNext.jsのプロジェクトに以下を記述します。
app/page.tsx
'use client';
import { useState } from 'react'
export default function Home() {
const [imageUrl, setImageUrl] = useState<string | null>(null)
const handleUpload = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
const fileInput = e.currentTarget.file as any
const file = fileInput.files[0]
const formData = new FormData()
formData.append('file', file)
const res = await fetch('/api/upload', {
method: 'POST',
body: formData,
})
const data = await res.json()
setImageUrl(data.url)
}
return (
<div className="m-12">
<form onSubmit={handleUpload} className="">
<input
type="file"
name="file"
accept="image/*"
required
className="file:mr-4 file:py-2 file:px-4 file:rounded-md file:text-sm file:bg-gray-300 file:text-black file:cursor-pointer"
/>
<button
type="submit"
className="block mt-8 py-2 px-6 bg-sky-600 rounded-full cursor-pointer text-white">
アップロード
</button>
</form>
{imageUrl && (
<div className="mt-6">
<p>アップロードされた画像</p>
<img src={imageUrl} alt="Uploaded" width={300} />
</div>
)}
</div>
)
}
app/api/upload/route.ts
import { NextRequest, NextResponse } from 'next/server'
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3'
const s3 = new S3Client({
region: process.env.AWS_REGION!,
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
},
})
export async function POST(req: NextRequest) {
const formData = await req.formData()
const file = formData.get('file') as File
if (!file) {
return NextResponse.json({ error: 'No file provided' }, { status: 400 })
}
const arrayBuffer = await file.arrayBuffer()
const buffer = Buffer.from(arrayBuffer)
const key = `uploads/${Date.now()}-${file.name}`
const params = {
Bucket: process.env.S3_BUCKET_NAME!,
Key: key,
Body: buffer,
ContentType: file.type,
}
try {
await s3.send(new PutObjectCommand(params))
const url = `https://${process.env.S3_BUCKET_NAME}.s3.${process.env.AWS_REGION}.amazonaws.com/${key}`
return NextResponse.json({ url })
} catch (error) {
console.error(error)
return NextResponse.json({ error: 'Upload failed' }, { status: 500 })
}
}
以上で実装は完了です。
これでNext.jsのアプリ上からS3に画像をアップロードすることができました!
Discussion