Next.jsからCloudflare R2に画像をアップロードして使用
はじめに
この記事では、next.jsからCloudflare R2に画像をアップロードしてサイト内で使用する方法を解説します。
手順
1.ライブラリのインストール
Cloudflare R2はAWS S3のAPIと互換性があり、aws-sdkを使用することができます。
npm install @aws-sdk/client-s3
2.クレカ登録とか
以下のページより、R2の利用を開始しましょう
クレジットカードの登録をすると利用可能になります
料金設定はこのようになっており、個人で開発する程度では無料で使用することができます
無料枠 | 月額 | |
---|---|---|
ストレージ | 10 GB / 月 | $0.015 / GB ストレージ |
クラスA操作:状態を変更する | 100万 / 月 | $4.50 / 100万 |
クラスB操作:既存の状態を読み取る | 1000万 / 月 | $0.36 / 100万 |
3.バケットの設定
ログインしたら左のサイドバーから、R2のOverviewページに進みましょう
次に、Create Bucketを押して次の画面に進んでください。
そうしたら、Bucket nameに任意の名前を入力し、Create Bucketを押してください
作成したバケットのSettingsに移動して、R2.dev subdomainを有効にしましょう。
Allow Accessを押すと有効にできます
出てきた、Public R2.dev Bucket URLを環境変数に登録しましょう
next.jsのプロジェクトのルートに.envファイルを作成して追加します。
R2_BUCKET_URL=https://pub-XXXX.r2.dev
4.APIトークンの作成
R2のOverview内のManage R2 API Tokensから、APIトークンの作成ができます
PermissionsをObject Read & WriteにしてAPIトークンを作成してください。
作成したトークンを環境変数に追加しましょう。
R2_ACCESS_KEY=XXXX
R2_SECRET_KEY=XXXX
R2_ENDPOINT=https://XXXX.r2.cloudflarestorage.com
5.APIの作成
Next.jsで画像をアップロードするためのAPIを作成しましょう
import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
import { NextRequest, NextResponse } from "next/server";
export async function POST(req: NextRequest) {
try {
const formData = await req.formData();
const imageFileData = formData.get("imageFileData") as File;
const imageFileDataArrayBuffer = await imageFileData.arrayBuffer();
const imageFileDataBuffer = Buffer.from(imageFileDataArrayBuffer);
const s3 = new S3Client({
region: "auto",
endpoint: process.env.R2_ENDPOINT!,
credentials: {
accessKeyId: process.env.R2_ACCESS_KEY!,
secretAccessKey: process.env.R2_SECRET_KEY!,
},
});
const key = `images/${Date.now()}_${imageFileData.name}`;
await s3.send(
new PutObjectCommand({
Bucket: "test",
Key: key,
ContentType: imageFileData.type,
Body: imageFileDataBuffer,
ACL: "public-read",
})
);
const uploadedUrl = `${process.env.R2_ENDPOINT}/test/${key}`;
const uploadedUrlUse = `${process.env.R2_ENDPOINT_DEV}/${key}`;
return NextResponse.json({
message: "アップロードに成功しました。",
url: uploadedUrlUse,
});
} catch (error) {
console.error("Error uploading file:", error);
return NextResponse.json(
{
message: "アップロードに失敗しました。",
error: error.message,
},
{ status: 500 }
);
}
}
5.アップロードフォームの作成
"use client";
import { useState } from "react";
import { TbPhotoPlus } from "react-icons/tb";
export default function Home() {
const [uploading, setUploading] = useState(false);
const [url, setUrl] = useState("");
const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (!file) return;
setUploading(true);
const formData = new FormData();
formData.append("imageFileData", file);
try {
const res = await fetch("/api/upload", {
method: "POST",
body: formData,
});
if (res.ok) {
const data = await res.json();
setUrl(data.url);
} else {
console.error("Upload failed.");
}
} catch (error) {
console.error("An error occurred while uploading the image:", error);
} finally {
setUploading(false);
}
};
return (
<div className="flex items-center justify-center h-screen">
<div className="relative bg-white p-4 border rounded-lg shadow-lg max-w-lg w-full">
{url ? (
<>
<h2 className="text-lg font-semibold mb-4 text-center">
アップロードされた画像
</h2>
<div className="flex justify-center">
<img
src={url}
alt="Uploaded"
className="object-cover max-h-80 max-w-full"
/>
</div>
</>
) : (
<>
<h2 className="text-lg font-semibold mb-4 text-center">
画像をアップロード
</h2>
<input
type="file"
onChange={handleFileChange}
className="hidden"
id="file-input"
accept="image/*"
/>
<label htmlFor="file-input">
<div
className={`relative flex h-80 cursor-pointer flex-col items-center justify-center gap-4 border-2 border-dashed border-neutral-300 transition hover:opacity-70 ${
uploading ? "opacity-50" : ""
}`}
>
<TbPhotoPlus size={50} />
</div>
</label>
</>
)}
</div>
</div>
);
}
おわりに
サブドメインを介したパブリック アクセスはr2.devレート制限されており、開発目的でのみ使用する必要があります。
アクセス管理、キャッシュ、ボット管理機能を有効にするには、バケットへのパブリック アクセスを有効にするときにカスタム ドメインを設定する必要があります。
今回、アップロードした画像を表示するのに使用しているr2.dev
は上記の通り、レート制限されています。
実際に公開するアプリなどで使用する場合は、別にドメインを設定しましょう
参考文献
Discussion