⚡
【Next.js14】Next.js API RouteからAWS S3バケットへアップロードする方法
はじめまして、Shinです。
今回はNext.js API RouteからAWS S3バケットへアップロードする方法を紹介します。
クライアント側のform送信ロジック
Next.jsのformタグは以下のようになっています。バリデーションチェックもしたいのでreact-hook-form
ライブラリを利用しています。
app/ap
"use client";
import React, { useRef, useState } from "react";
import { useForm } from "react-hook-form";
const CreateBook = () => {
const {
register,
handleSubmit,
formState: { errors },
} = useForm();
const inputFileRef = useRef<HTMLInputElement>(null);
const [blob, setBlob] = useState<any>(null);
const onSubmit = async (data: any) => {
const { title, content, price } = data;
if (!inputFileRef.current?.files) {
throw new Error("No file selected");
}
const file = inputFileRef.current.files[0];
const imageFileName = file.name;
const formData = new FormData();
formData.append("file", file); // ここでファイルオブジェクトを追加
//ここで画像アップロードAPIを叩く(後半で記載しています)
const imageUploadResponse = await fetch(
`${process.env.NEXT_PUBLIC_API_URL}/thumbnail/upload?filename=${imageFileName}`,
{
method: "POST",
body: formData, //ここにセット
}
);
...省略
};
...省略
return (
<form onSubmit={handleSubmit(onSubmit)} className="mt-10 container mx-auto">
<div className="mt-4">
<label htmlFor="image">サムネイル</label>
//下記にname="file"を設定
<input ref={inputFileRef} type="file" id="image" name="file" required />
{errors.image && (
<p className="text-red-500 font-medium">Price is required.</p>
)}
{blob && (
<div>
Blob url: <a href={blob.url}>{blob.url}</a>
</div>
)}
</div>
<button
type="submit"
className="bg-blue-400 px-6 py-3 font-medium rounded-md text-white shadow-sm mt-4"
>
Create Book
</button>
</form>
);
};
export default CreateBook;
useRef()を使って、input属性を取得。そしてconst file = inputFileRef.current.files[0];
でファイルオブジェクトを取得しています。
それをformData.append("file", file);
で設定し、fetchのbodyへ付けてAPI側で受け取ります。
次に、Next.js API RouteでAPI設定をします。
APIの設定
Next.js13以降で導入された/appディレクトリでAPI Route設定をします。
app/api/thumbnail/upload/route.ts
import { NextResponse } from "next/server";
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
//Upload image to AWS S3
export async function POST(request: Request) {
const { ACCESS_KEY_ID, SECRET_ACCESS_KEY, REGION, S3_BUCKET_NAME } =
process.env;
const s3Client = new S3Client({
region: REGION,
credentials: {
accessKeyId: ACCESS_KEY_ID || "",
secretAccessKey: SECRET_ACCESS_KEY || "",
},
});
// URLからファイル名を取得
const { searchParams } = new URL(request.url);
const fileName = searchParams.get("filename");
const formData = await request.formData();
const file: any = formData.get("file");
// File オブジェクトから Buffer に変換
const buffer = Buffer.from(await file?.arrayBuffer());
// アップロードパラメータの設定
const uploadParams: any = {
Bucket: S3_BUCKET_NAME,
Key: fileName, //保存時の画像名
Body: buffer, //input fileから取得
ContentType: "image/png", // 適切なContentTypeを設定
ACL: "public-read", // 公開読み取りアクセスを設定
};
try {
//画像のアップロード
const command = new PutObjectCommand(uploadParams);
const uploadResult = await s3Client.send(command);
console.log("Upload success:", uploadResult);
const imageUrl = `https://${S3_BUCKET_NAME}.s3.${REGION}.amazonaws.com/${fileName}`;
return NextResponse.json({ imageUrl });
} catch (err) {
console.error(err);
return NextResponse.json(err);
}
}
重要な記述が以下でしょう。
const formData = await request.formData();
const file: any = formData.get("file");
// File オブジェクトから Buffer に変換
const buffer = Buffer.from(await file?.arrayBuffer());
S3に送る際はbuffer形式でないとダメみたいです。そうでないとアップロードできたとしても画像が表示されません。
また、公開読み取りアクセス設定の箇所のALCなのですが、これはS3バケットの方で有効に✅を入れておきましょう。じゃないと弾かれてエラーになります。下画像のようにしてください。
アクセス許可タブからオブジェクト所有者で確認できます。
実際に画像投稿してみる
やってみます。
上のCreate Bookボタンを押すと
このように画像アップロードできます。S3はこうなっています。
ということで、今回は以上になります。
Udemyクーポン配布中です。
気になる方は下記をチェックしてみてください。
また、Youtubeも運営しているのでぜひ!
Discussion