色々面倒だった
React.js で画像をアップロードしたかったが、S3 のクレデンシャル情報は隠匿したかったので、アップロード自体は、サーバサイド(API)で行った。
やってみたら結構面倒臭かった。
環境
- React.js: 18.0.0
- Next.js: 12.1.6
- formidable: 2.0.1
React.js 側は axios で Next.js で実装した API を呼ぶ。
Next.js 側で受け取ってたら S3 にアップロードする。
React 側
まず、File タイプを POST する場合、"Content-Type": "multipart/form-data"
でないと送れない。json
形式だと送れない。
例えば、リクエストパラメータとして、file: File
, type: string
を送りたい場合、
axios.post('/api/upload', {
file: somefile,
path: 'dir/'
}, {
headers: {
'Content-Type': 'multipart/form-data',
}
});
などとしても送れそうだけど、送れない。FormData に append して送る。
const params = new FormData();
params.append('file', somefile);
params.append('path', 'dir/');
axios.post('/api/upload',
params,
{
headers: {
'Content-Type': 'multipart/form-data',
}
}
Next 側
にあるように、まず、
export const config = {
api: {
bodyParser: false,
},
};
こうしてあげる必要がある。
なお、s3 にアップロードするのに必要な package は、aws-sdk
, formidable
, fs
なので、@type/aws-sdk
, @types/formidable
と共にインストールしておく。
const form = formidable();
form.parse(req, async (err, fields, files: any) => {
if (...) {
// 必要なエラー処理
}
try {
// S3 にアップロードする処理
} catch (err) {
// S3 アップロードに失敗した時の処理
}
}
流れはこんな感じ。
React 側で送った file と path を POST しているが、file
は、files.file
に、FILE タイプ以外の path
は、fields.path
に入る。file を hoge で送れば、files.hoge。
S3 に put する Body は、fs で。
s3.putObject({
ACL: 'public-read',
Bucket: process.env.S3_BUCKET,
Key: `${fields.path}${files.file.originalFilename}`,
ContentType: files.file.minetype,
Body: fs.createReadStream(files.file.filepath),
});
files: any
などとしておかないと、formidable の originalFilename
とか filepath
などのプロパティにアクセスしようとした時 Type Error になる。取得できるけど、build できない。any じゃなくて、厳密に files: formidable.Files
としたいところだが、うまくいかない。
なお、File をアップロードするだけの API であれば、response は 201 が良いかと。
おしまい。