本文へ移動
verified onnext@16.3.5react@19
最終検証日 2026-09-26
Next.js道場

Next.js のファイルアップロード。最小構成サンプル

ファイルを受け取る最小の形は、保存しないことから始まります。

動いているものを触る/demo/upload

ファイル構成

並んでいるのは、上の画面をいま動かしている本物のファイルです。名前を押すと中身が出ます。

ファイル構成
  • app/
app/layout.tsxBuild

全ページの外側。本物のプロジェクトでは 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: "回数が多すぎます。少し待ってください。" }
}
送信ボタンを押してから、結果が出るまで
  1. Browser
    ファイルを選んで送る

    選んだ中身がそのまま通信に乗る。

  2. Server
    回数・形・大きさを見る

    ここを通らないものは何もしない。

  3. Server
    分かったことだけ返す

    保存しない。中身も読まない。

  4. 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つ目の戻り値が「送信中かどうか」です。ボタンを押せなくするのに使っています。

ここから足していくとき

  • 保存する。置き場所・容量の上限・消し方を先に決めてから足します
  • 配信する。別のドメインから配る形にします。同じドメインから配らない
  • 中身を確かめる。先頭のバイト列を見て、申告された種類と合っているかを見ます

足す順番はこの通りです。保存より先に配信を足すと、守るものが一気に増えます。

入れていないもの

このまま本番に持っていかないでください。省いてあるのは次の点です。

  • 保存。ディスクにもデータベースにも書いていません
  • 配信。受け取ったものを見せ返す口はありません
  • 中身の検査。ウイルス検査も、本当にその種類かの確認もしていません
  • 種類の制限。申告された種類をそのまま出しているだけです

出典

このページの内容は上記の一次情報を基準に書いています。個人ブログや二次情報を根拠にしていません。

理解できたか試す

では、このコードはどうなりますか。

useActionState に渡した Server Function が実際に何を受け取るかを調べています。ブラウザから送信すると、戻り値はどうなりますか。

この問題を解くこのテーマの問題 3 問