Next.js のファイルアップロード。最小構成サンプル
ファイルを受け取る最小の形は、保存しないことから始まります。
動いているものを触る/demo/uploadファイル構成
並んでいるのは、上の画面をいま動かしている本物のファイルです。名前を押すと中身が出ます。
- app/
全ページの外側。本物のプロジェクトでは html と body をここで書きます
import type { Metadata } from "next";
export const metadata: Metadata = {
title: "アップロードの最小構成",
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return <main>{children}</main>;
}- .tsx
- 画面を返すファイル。HTML のような書き方ができます
- .ts
- 画面を返さないファイル。処理とデータだけを置きます。ここに画面を書くと原因を名指ししないエラーで落ちます
- Build
- ビルド時に一度だけ動きます
- Server
- アクセスのたびにサーバーで動きます
- Browser
- 利用者の手元で動きます
最小は「保存しない」から始まる
アップロードと聞くと、まず置き場所を決めたくなります。その前に決めることがあります。
| やること | 必要か | 増える守り |
|---|---|---|
| 受け取って調べる | できる | 回数と大きさ |
| 保存する | できない | 置き場所・容量・消し方・中身の検査 |
| 配信する | できない | 誰に見せるか・何を実行させないか |
このサンプルは1行目だけをやります。保存しないので、保存したものを踏ませる経路がそもそも生まれません。
誰でも呼べる入口だという前提
app/actions.ts は、画面を経由せずに呼べます。だから最初の2行が回数の制限です。
if (!take(`demo-upload:${await clientIp()}`, 10, 60_000)) {
return { ok: false, message: "回数が多すぎます。少し待ってください。" }
}
- Browserファイルを選んで送る
選んだ中身がそのまま通信に乗る。
- Server回数・形・大きさを見る
ここを通らないものは何もしない。
- Server分かったことだけ返す
保存しない。中身も読まない。
- Browserその場に結果が出る
ページは移らない。
上限は2段ある
const MAX_BYTES = 512 * 1024
if (file.size > MAX_BYTES) {
return { ok: false, message: "512KB までにしてください。" }
}
これはアプリ側の上限です。その手前に、枠組み自体の上限があります。Server Function の本文は既定で 1MB までで、超えたものはこの関数に届く前に断られます。
種類は自己申告
file.type は、ブラウザが「たぶんこれ」と言ってきた値です。中身を見た結果ではありません。
const kind = file.type || "申告なし"
だから画面にも「〜と申告されました」と出しています。本当にその種類かを確かめるには、中身の先頭を読んで判定する必要があります。このサンプルはそこまでやりません。
拡張子も同じです。.png という名前の実行ファイルは作れます。
名前をパスとして使わない
実機で確かめました。../../../etc/passwd という名前のファイルを選んで送ると、サーバーには passwd として届きます。ブラウザが送る前にパスを落としています。
長さも切ります。200文字の名前を送ると、画面には 60 文字までしか出ません。
結果をその場に出す
ここで2つ目の Client Component が出てきます。検索と同じで、必要な部分だけです。
const [result, action, pending] = useActionState(inspect, FIRST)
useActionState に渡した関数は、第1引数に前の状態、第2引数にフォームの内容を受け取ります。順番を逆にすると動きません。詳しくはServer Function の呼び方にあります。
3つ目の戻り値が「送信中かどうか」です。ボタンを押せなくするのに使っています。
ここから足していくとき
- 保存する。置き場所・容量の上限・消し方を先に決めてから足します
- 配信する。別のドメインから配る形にします。同じドメインから配らない
- 中身を確かめる。先頭のバイト列を見て、申告された種類と合っているかを見ます
足す順番はこの通りです。保存より先に配信を足すと、守るものが一気に増えます。
入れていないもの
このまま本番に持っていかないでください。省いてあるのは次の点です。
- 保存。ディスクにもデータベースにも書いていません
- 配信。受け取ったものを見せ返す口はありません
- 中身の検査。ウイルス検査も、本当にその種類かの確認もしていません
- 種類の制限。申告された種類をそのまま出しているだけです
出典
- nextjs.org/docs/app/api-reference/config/next-config-js/serverActions
- react.dev/reference/react/useActionState
- developer.mozilla.org/ja/docs/Web/API/File
このページの内容は上記の一次情報を基準に書いています。個人ブログや二次情報を根拠にしていません。