Upload files to AWS S3 with Next.js
Add packages.
bun add @aws-sdk/s3-presigned-post
bun add uuid @types/uuid
Create environment variables.
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_REGION=
AWS_BUCKET_NAME=
NEXT_PUBLIC_BASE_URL=http://localhost:3000
Create file upload form.
// src/app/page.tsx
'use client'
import { useState, useRef} from 'react'
export default function Page() {
const [file, setFile] = useState<File | null>(null)
const [uploading, setUploading] = useState(false)
const inputFileRef = useRef(null); // TODO:なにこれ?
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
if (!file) {
alert('Please select a file to upload.')
return
}
setUploading(true)
const response = await fetch(
process.env.NEXT_PUBLIC_BASE_URL + '/api/upload',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ filename: file.name, contentType: file.type }),
}
)
if (response.ok) {
const { url, fields } = await response.json()
const formData = new FormData()
Object.entries(fields).forEach(([key, value]) => {
formData.append(key, value as string)
})
formData.append('file', file)
const uploadResponse = await fetch(url, {
method: 'POST',
body: formData,
})
if (uploadResponse.ok) {
// アップロード完了後に input 要素のテキストをクリア
if (inputFileRef.current) {
(inputFileRef.current as HTMLInputElement).value = '';
}
alert('Upload successful!')
} else {
console.error('S3 Upload Error:', uploadResponse)
alert('Upload failed.')
}
} else {
alert('Failed to get pre-signed URL.')
}
setUploading(false)
}
return (
<main>
<h1>Upload a File to S3</h1>
<form onSubmit={handleSubmit}>
<input
id="file"
type="file"
ref={inputFileRef} // input 要素への ref を設定
onChange={(e) => {
const files = e.target.files
if (files) {
setFile(files[0])
}
}}
accept="image/png, image/jpeg"
/>
<button type="submit" disabled={uploading}>
Upload
</button>
</form>
</main>
)
}
Create API route to get pre-signed URL.
// src/app/api/upload/route.ts
import { createPresignedPost } from '@aws-sdk/s3-presigned-post'
import { S3Client } from '@aws-sdk/client-s3'
import { v4 as uuidv4 } from 'uuid'
export async function POST(request: Request) {
const { filename, contentType } = await request.json()
try {
const client = new S3Client({ region: process.env.AWS_REGION })
const { url, fields } = await createPresignedPost(client, {
Bucket: process.env.AWS_BUCKET_NAME || '',
Key: uuidv4(),
Conditions: [
['content-length-range', 0, 10485760], // up to 10 MB
['starts-with', '$Content-Type', contentType],
],
Fields: {
acl: 'public-read',
'Content-Type': contentType,
},
Expires: 600, // Seconds before the presigned post expires. 3600 by default.
})
return Response.json({ url, fields })
} catch (error) {
const e = error as Error;
return Response.json({ error: e.message })
}
}