Vercel-Datei-Upload-Konfiguration und Limits | Vercel Blob, API-Limits und Workarounds
Beim Implementieren von File-Upload in einer auf Vercel bereitgestellten App gibt es ein Limit für die Größe des Request-Body von Serverless Functions. Dieses Limit von 4,5 MB ist standardmäßig häufig eine Falle. Dieser Artikel erklärt im Detail das Request-Body-Größenlimit von Vercel Function, wie man es in <code>vercel.json</code> konfiguriert, wie man Vercel Blob verwendet, und wie man die Limitation umgeht, um große Dateien zu verarbeiten (direktes Upload zu S3).
Größenlimit für Vercel Function Request-Body
Vercel Serverless Functions (API Routes, Route Handlers, Edge Functions) haben ein Limit für die Request-Größe. Requests, die dieses Limit überschreiten, führen zu einem 413-Fehler.
| Arten von Function | Standard-Limit | Maximaler Konfigurationswert | Anmerkungen |
|---|---|---|---|
| Serverless Function(Node.js) | 4.5 MB | 4.5 MB | Kann nicht in <code>vercel.json</code> geändert werden |
| Edge Function | 4 MB | 4 MB | Kann nicht geändert werden |
| Next.js Server Actions | 1 MB | Konfigurierbar | Änderung in next.config.js |
Das Wichtigste ist, dass die Größenbeschränkung des Bodys der Vercel Serverless Function eine <strong>Einschränkung auf Infrastrukturebene</strong> ist und nicht durch Konfigurationsänderungen im Anwendungscode umgangen werden kann. Um Dateien größer als 4.5MB hochzuladen, ist eine architektonische Umgehungslösung erforderlich.
Konfiguration in <code>vercel.json</code>
In <code>vercel.json</code> können Sie die Ausführungszeit (<code>maxDuration</code>) und den Speicher (<code>memory</code>) der Function konfigurieren, aber nicht das Limit der Body-Größe ändern. Bei Next.js Pages Router gelten jedoch einige Einstellungen wie <code>responseLimit</code>.
// vercel.json
{
"functions": {
"app/api/upload/route.ts": {
"maxDuration": 60,
"memory": 1024
},
"pages/api/upload.ts": {
"maxDuration": 30
}
},
"headers": [
{
"source": "/api/(.*)",
"headers": [
{
"key": "Access-Control-Allow-Origin",
"value": "*"
}
]
}
]
}
// next.config.js(Next.js の Server Actions のボディサイズを変更)
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
serverActions: {
// Server Actions のボディサイズ上限を変更(Vercel インフラの制限は超えられない)
bodySizeLimit: '4mb',
},
},
};
module.exports = nextConfig;
// Pages Router の API Route でのボディパーサー無効化
// pages/api/upload.ts
import type { NextApiRequest, NextApiResponse } from 'next';
export const config = {
api: {
// bodyParser を無効にしてストリームとして受け取る
bodyParser: false,
// responseLimit: false, // レスポンスサイズ制限を無効化
},
};
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== 'POST') {
return res.status(405).json({ error: 'Method not allowed' });
}
// formidable などでマルチパートを解析
const { IncomingForm } = await import('formidable');
const form = new IncomingForm({ maxFileSize: 4 * 1024 * 1024 }); // 4MB 上限
form.parse(req, (err, fields, files) => {
if (err) {
return res.status(400).json({ error: err.message });
}
const file = Array.isArray(files.file) ? files.file[0] : files.file;
if (!file) return res.status(400).json({ error: 'ファイルが見つかりません。' });
res.json({ filename: file.originalFilename, size: file.size });
});
}
So verwenden Sie Vercel Blob
Vercel Blob ist ein von Vercel bereitgestellter Objektspeicherdienst. Mit dem Paket <code>@vercel/blob</code> können Sie Dateien von Ihrem Vercel-App speichern und bereitstellen, ohne S3 zu konfigurieren.
npm install @vercel/blob
// サーバーサイドアップロード(4.5MB 以下のファイル向け)
// app/api/upload/route.ts
import { put, del, list } from '@vercel/blob';
import { NextRequest, NextResponse } from 'next/server';
export async function POST(request: NextRequest) {
const form = await request.formData();
const file = form.get('file') as File;
if (!file) {
return NextResponse.json({ error: 'ファイルが見つかりません。' }, { status: 400 });
}
// Vercel Blob にアップロード
// 環境変数 BLOB_READ_WRITE_TOKEN が必要
const blob = await put(file.name, file, {
access: 'public', // 'public'(誰でも閲覧可)または 'private'
addRandomSuffix: true, // ファイル名にランダムなサフィックスを追加
contentType: file.type, // MIMEタイプを明示
});
return NextResponse.json({
url: blob.url, // 配信 URL
downloadUrl: blob.downloadUrl, // ダウンロード URL
pathname: blob.pathname, // パス名
size: blob.size, // ファイルサイズ(バイト)
contentType: blob.contentType,
});
}
// ファイル一覧の取得
export async function GET() {
const { blobs } = await list({ prefix: 'uploads/' });
return NextResponse.json(blobs);
}
// ファイルの削除
export async function DELETE(request: NextRequest) {
const { url } = await request.json();
await del(url);
return NextResponse.json({ deleted: true });
}
// クライアントアップロード(4.5MB 超のファイル向け)
// Vercel Blob がトークンを発行してクライアントから直接アップロード
// app/api/upload/route.ts
import { handleUpload, type HandleUploadBody } from '@vercel/blob/client';
import { NextRequest, NextResponse } from 'next/server';
export async function POST(request: NextRequest): Promise<NextResponse> {
const body = (await request.json()) as HandleUploadBody;
try {
const jsonResponse = await handleUpload({
body,
request,
onBeforeGenerateToken: async (pathname, clientPayload) => {
// ここでユーザー認証やバリデーションを行う
return {
allowedContentTypes: ['image/jpeg', 'image/png', 'image/webp', 'application/pdf'],
maximumSizeInBytes: 100 * 1024 * 1024, // 100MB
tokenPayload: JSON.stringify({ userId: 'user-123' }),
};
},
onUploadCompleted: async ({ blob, tokenPayload }) => {
// アップロード完了後の処理(DB への保存など)
const { userId } = JSON.parse(tokenPayload ?? '{}');
console.log(`ユーザー ${userId} が ${blob.url} をアップロードしました`);
// await db.files.create({ userId, url: blob.url });
},
});
return NextResponse.json(jsonResponse);
} catch (error) {
return NextResponse.json({ error: String(error) }, { status: 400 });
}
}
// クライアントコンポーネントでのクライアントアップロード
// components/BlobUpload.tsx
'use client';
import { upload } from '@vercel/blob/client';
import { useState } from 'react';
export default function BlobUpload() {
const [url, setUrl] = useState('');
const [progress, setProgress] = useState(0);
const handleUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (!file) return;
const blob = await upload(file.name, file, {
access: 'public',
handleUploadUrl: '/api/upload', // トークン発行エンドポイント
onUploadProgress: ({ percentage }) => {
setProgress(Math.round(percentage));
},
});
setUrl(blob.url);
};
return (
<div>
<input type="file" onChange={handleUpload} />
{progress > 0 && progress < 100 && <p>{progress}% アップロード中...</p>}
{url && <a href={url}>アップロードされたファイル</a>}
</div>
);
}
Workaround für große Dateien: Direkter Upload zu S3
Wenn Sie Vercel Blob nicht verwenden oder S3 direkt verwenden möchten, ist das direkte Hochladen vom Client zu S3 über eine Presigned URL die zuverlässigste Methode. Da Vercel Function nur Token generiert, ist sie nicht von der Größenbeschränkung des Request-Body betroffen.
// app/api/presigned-url/route.ts
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
import { NextRequest, NextResponse } from 'next/server';
const s3 = new S3Client({ region: process.env.AWS_REGION! });
export async function POST(request: NextRequest) {
// このリクエストはファイル本体を含まないため、Vercel の上限に引っかからない
const { filename, contentType, size } = await request.json();
const key = `uploads/${Date.now()}-${filename}`;
const command = new PutObjectCommand({
Bucket: process.env.AWS_BUCKET_NAME!,
Key: key,
ContentType: contentType,
});
const presignedUrl = await getSignedUrl(s3, command, { expiresIn: 3600 });
return NextResponse.json({ presignedUrl, key });
}
// クライアントコードでは:
// 1. /api/presigned-url に POST して URL を取得
// 2. 取得した URL に XHR で PUT リクエストを送信(進捗バー付き)
// → Vercel Function を経由しないため容量制限なし
Testdatei zur Verwendung in diesem Artikel (kostenlos)
- → <a href="/ja/files/images/png/1mb/" class="text-primary-600 dark:text-primary-400 hover:underline">PNG-Testbild (1MB)</a> — Zur Überprüfung des Upload-Verhaltens zu <code>Vercel Blob</code>
- → <a href="/ja/files/pdf/1mb/" class="text-primary-600 dark:text-primary-400 hover:underline">PDF-Testdatei (1MB)</a> — Zum Testen von Datei-Uploads innerhalb des Limits von 4.5MB
- → <a href="/ja/files/zip/1mb/" class="text-primary-600 dark:text-primary-400 hover:underline">ZIP-Testdatei (1MB)</a> — Für S3-Direktupload-Test über Presigned URL
Verwandte Artikel
- → <a href="/ja/blog/nextjs-file-upload/" class="text-primary-600 dark:text-primary-400 hover:underline">Implementierungsleitfaden für Datei-Uploads in Next.js | App Router, API Route und S3-Unterstützung</a>
- → <a href="/ja/blog/s3-upload-limit/" class="text-primary-600 dark:text-primary-400 hover:underline">Zusammenfassung der Datei-Upload-Limits von AWS S3 und CloudFront</a>
- → <a href="/ja/blog/netlify-upload-config/" class="text-primary-600 dark:text-primary-400 hover:underline">Datei-Upload mit Netlify Functions | Limits, Large Media und Workarounds</a>