覚書です。
目次
Next.jsでCloudFlareR2画像アップロード
ちょっと苦労しました。firebaseと違ってあっさりと実装とはいきませんでした。間違った方向に進むとハマりやすいです。
ひとまず、サーバーサイドのコード。
import type { NextApiRequest, NextApiResponse } from 'next';
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
const { IncomingForm } = require('formidable') as any; // formidableはTSに対応していないライブラリ
import fs from 'fs';
import { UPLOAD_PATH, BUCKET_NAME } from '../../const/constants';
const s3Client = new S3Client({
credentials: {
accessKeyId: process.env.CLOUDFLARE_ACCESS_KEY_ID!,
secretAccessKey: process.env.CLOUDFLARE_SECRET_ACCESS_KEY!
},
endpoint: process.env.CLOUDFLARE_R2_ENDPOINT!,
region: 'auto'
});
export const config = {
api: {
bodyParser: false
}
};
export default function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== 'POST') {
res.setHeader('Allow', ['POST']);
res.status(405).end(`Method ${req.method} Not Allowed`);
return;
}
const form = new IncomingForm();
new Promise((resolve, reject) => {
form.parse(req, (err: any, fields: any, files: any) => {
if (err) reject(err);
resolve({ fields, files });
});
})
.then((data: any) => handleUpload(data.fields, data.files, res))
.catch(error => {
console.error('Formidable error:', error);
res.status(500).json({ error: 'Form processing error' });
});
}
async function handleUpload(fields: any, files: any, res: NextApiResponse) {
const fileName = fields.fileName[0];
const contentType = fields.contentType[0];
const file = files.file[0];
try {
const fileStream = fs.createReadStream(file.filepath);
const uploadCommand = new PutObjectCommand({
Bucket: BUCKET_NAME,
Key: UPLOAD_PATH + fileName,
Body: fileStream,
ContentType: file.mimetype
});
await s3Client.send(uploadCommand);
res.status(200).json({ message: 'File uploaded successfully' });
} catch (error) {
console.error('Error in uploadR2:', error);
res.status(500).json({ error: 'Internal server error' });
}
}
クライアント(FormDataを作成) ⇒ pages/api/uploadR2.ts サーバーサイド ⇒ R2
BlobとFormDataとArrayBufferの比較と違い
- Blobを使用: 直接Blobデータをアップロードする方法です。この方法は比較的シンプルで、クライアントサイドでの実装が容易ですが、サーバーサイドでの処理がやや複雑になります。
- FormDataを使用: FormDataを使用してファイルとメタデータを一緒に送信する方法です。サーバーサイドでの処理が容易になるメリットがありますが、クライアントサイドの実装が少し複雑になる可能性があります。
- ArrayBufferを使用: ArrayBufferを使用してバイナリデータをアップロードする方法です。この方法は高度なデータ操作に適していますが、実装が最も複雑です。
Body exceeded 1mb limit
Body exceeded 1mb limit
クライアントからサーバーにデータを送るときにでたエラー。
Next.jsのAPIルートはデフォルトで最大1MBのリクエストボディサイズに制限されています。この問題を解決するには、APIルートで許可されるリクエストボディのサイズを増やす必要があります。
module.exports = {
api: {
bodyParser: {
sizeLimit: '10mb', // 10MBまでのボディサイズを許可
},
},
};
ただし、アップロードされた画像は50kb。中間ファイルとしてもこの制限にひっかかるのはおかしいので要調査項目ですかね…。
Next.jsのクライアントからR2はおすすめしない
Next.jsのクライアントからR2はおすすめしないです。セキュリティの問題ではじかれました。署名付きURLをサーバーサイドで作ってクライアントからアップロードすることならできたため、方法がないわけではありません。
署名付きURLは期限つきのため仕様によりけりです。
Next.jsでCloudflare Workers経由で画像をアップロードはおすすめしない
Next.jsでCloud Worker経由で画像をアップロードする方法を試しましたが、ハマりました…。GPT4君につられてこっちで挑戦してしまったけど、途中でヤバイことに気が付いて作戦変更。やっぱAIは注意しないとね。
たぶんできないことはないんだろうけど、実装がややこしくなりそうです。スマートじゃないとわかって思い切って全部捨てました…。
Cloudflare Workersはセキュリティ的にaws-sdkをインポートして使えないようです。
FormDataもちょっと問題ありました。
cloudflare workersのFormDataがFile
— kasu (@ergofriend) May 1, 2022
じゃなくてstringを返してくるの困るな
CloudFlareでドメイン管理が必要
Firebaseのアップロードとは違ってカスタムドメインを使う必要があります。元からあるr2.dev サブドメインは本番環境で推奨されていません。
このカスタムドメインはCloudFlareで管理されているドメインしか使えません。
新規ドメインを取得するか、移管するかという煩わしさがありました。
無料枠は魅力的ですが、ちょっとCloudFlareはややこしさを感じますね。そだけセキュリティが厳しいとうことかもしれませんが…。でも、できました。
簡単なまとめですが、ご参考になれば幸いです。
コメント