Skip to content

Datei-Upload mit Netlify Functions | Grenzen, Large Media und Workarounds

Kategorie:Netlify · Deployment-Konfiguration
Dieser Artikel ist derzeit nur auf Japanisch verfügbar. Übersetzte Versionen werden schrittweise veröffentlicht.

Bei der Implementierung von Datei-Upload in Netlify Functions (serverlose Funktion basierend auf AWS Lambda) gibt es ein Limit für die Request-Body-Größe. Viele Entwickler stoßen auf Probleme, da sie dieses Standard-Limit von 6MB nicht kennen. Dieser Artikel erklärt das Body-Size-Limit von Netlify Functions, Konfiguration über <code>netlify.toml</code>, Parse-Methoden für <code>multipart/form-data</code> und Alternativen für Dateien, die das Limit überschreiten.

Functions 6 MB request body 10s timeout Background Fn 6 MB request body 15min timeout Edge Functions 20 MB request body streaming OK
Abbildung: Größenlimit für Request-Body pro Netlify-Funktion

Größenlimit für Request-Body in Netlify Functions

Netlify Functions basiert auf AWS Lambda und hat eine feste Größenlimitierung für den Request-Body.

Arten von Function Größenlimit für Text Möglichkeit zur Änderung Anmerkungen
Netlify Functions (synchron) 6 MB Nicht zulässig AWS Lambda-Einschränkungen
Netlify Functions(Background) 6 MB Nicht zulässig Die Verarbeitungszeit beträgt maximal 15 Minuten
Netlify Edge Functions Mit Einschränkungen Nicht zulässig Deno-basierte Runtime

Das Senden von Anfragen größer als 6MB an Netlify Functions führt zu HTTP 413 oder Function-Timeout. Obwohl etwas größer als Vercel (4.5MB), können große Dateien wie Bilder und Videos nicht direkt empfangen werden.

Konfiguration von netlify.toml

In <code>netlify.toml</code> können Sie Runtime, Timeout, Speicher, Weiterleitungen, Header und mehr für Functions konfigurieren. Die Größe des Body selbst kann nicht geändert werden, aber es ist wichtig, die zugehörigen Einstellungen zu verstehen.

# netlify.toml

[build]
  command = "npm run build"
  publish = ".next"
  functions = "netlify/functions"  # Functions のディレクトリ

# Node.js バンドラーの設定
[functions]
  node_bundler = "esbuild"

# 特定の Function の設定
[functions."upload"]
  included_files = ["uploads/**"]

# リダイレクト設定(Next.js など SPA との組み合わせ)
[[redirects]]
  from = "/api/*"
  to = "/.netlify/functions/:splat"
  status = 200

# ヘッダーの設定(CORS など)
[[headers]]
  for = "/.netlify/functions/*"
  [headers.values]
    Access-Control-Allow-Origin = "*"
    Access-Control-Allow-Methods = "GET, POST, PUT, DELETE, OPTIONS"
    Access-Control-Allow-Headers = "Content-Type, Authorization"

# 環境変数(本番用。機密情報は Netlify UI で設定すること)
[context.production.environment]
  NODE_ENV = "production"

[context.deploy-preview.environment]
  NODE_ENV = "development"

Implementierung des multipart/form-data-Parsings

In Netlify Functions kann der Request-Body standardmäßig Base64-codiert sein. Wir erläutern Methoden zum Parsen von Multipart-Daten mit <code>busboy</code> oder <code>formidable</code>.

// netlify/functions/upload.js(CommonJS)
const busboy = require('busboy');

exports.handler = async (event) => {
  if (event.httpMethod !== 'POST') {
    return { statusCode: 405, body: 'Method Not Allowed' };
  }

  return new Promise((resolve, reject) => {
    const contentType = event.headers['content-type'] || event.headers['Content-Type'];

    const bb = busboy({ headers: { 'content-type': contentType } });
    const files = [];
    const fields = {};

    bb.on('file', (name, file, info) => {
      const { filename, encoding, mimeType } = info;
      const chunks = [];

      file.on('data', (data) => chunks.push(data));
      file.on('end', () => {
        const buffer = Buffer.concat(chunks);

        // ファイルサイズの検証(6MB 上限の手前で確認)
        const MAX_SIZE = 5 * 1024 * 1024; // 5MB(余裕を持たせる)
        if (buffer.length > MAX_SIZE) {
          resolve({
            statusCode: 413,
            body: JSON.stringify({ error: 'ファイルが大きすぎます(上限5MB)。' }),
          });
          return;
        }

        files.push({ name, filename, mimeType, buffer, size: buffer.length });
      });
    });

    bb.on('field', (name, value) => {
      fields[name] = value;
    });

    bb.on('finish', () => {
      if (files.length === 0) {
        resolve({
          statusCode: 400,
          body: JSON.stringify({ error: 'ファイルが見つかりません。' }),
        });
        return;
      }

      const file = files[0];
      // ここでファイルを S3 などに保存する処理を行う

      resolve({
        statusCode: 200,
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          success: true,
          filename: file.filename,
          size: file.size,
          mimeType: file.mimeType,
          fields,
        }),
      });
    });

    bb.on('error', (err) => {
      resolve({
        statusCode: 500,
        body: JSON.stringify({ error: err.message }),
      });
    });

    // Netlify Functions ではボディが Base64 エンコードされる場合がある
    const body = event.isBase64Encoded
      ? Buffer.from(event.body, 'base64')
      : event.body;

    bb.end(body);
  });
};
// netlify/functions/upload-s3.js(S3 に転送する例)
const busboy = require('busboy');
const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3');

const s3 = new S3Client({ region: process.env.AWS_REGION });

exports.handler = async (event) => {
  if (event.httpMethod !== 'POST') {
    return { statusCode: 405, body: 'Method Not Allowed' };
  }

  return new Promise((resolve) => {
    const bb = busboy({
      headers: { 'content-type': event.headers['content-type'] },
      limits: { fileSize: 5 * 1024 * 1024 }, // 5MB で切り捨て
    });

    bb.on('file', async (fieldName, stream, { filename, mimeType }) => {
      const chunks = [];
      let truncated = false;

      stream.on('data', (chunk) => chunks.push(chunk));
      stream.on('limit', () => { truncated = true; });

      stream.on('end', async () => {
        if (truncated) {
          return resolve({
            statusCode: 413,
            body: JSON.stringify({ error: 'ファイルが5MBを超えています。' }),
          });
        }

        const buffer = Buffer.concat(chunks);
        const key = `uploads/${Date.now()}-${filename}`;

        try {
          await s3.send(new PutObjectCommand({
            Bucket: process.env.AWS_S3_BUCKET,
            Key: key,
            Body: buffer,
            ContentType: mimeType,
          }));

          resolve({
            statusCode: 200,
            body: JSON.stringify({
              success: true,
              key,
              url: `https://${process.env.AWS_S3_BUCKET}.s3.amazonaws.com/${key}`,
            }),
          });
        } catch (err) {
          resolve({ statusCode: 500, body: JSON.stringify({ error: err.message }) });
        }
      });
    });

    const body = event.isBase64Encoded
      ? Buffer.from(event.body, 'base64')
      : event.body;

    bb.end(body);
  });
};

Netlify Large Media (Veraltet) und Alternativen

Netlify Large Media war ein Git-LFS-basierter Service zur Verwaltung großer Dateien, aber derzeit sind Neuanmeldungen eingeschränkt und eine Migration zu Alternativen wird empfohlen.

# Netlify Large Media(非推奨・新規利用不可)
# .lfsconfig
[lfs]
    url = https://large-media.netlify.com/<repo-id>

Die empfohlene Konfiguration als Alternative ist wie folgt.

// Presigned URL 経由で S3 に直接アップロードする Netlify Function
// netlify/functions/get-upload-url.js
const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3');
const { getSignedUrl } = require('@aws-sdk/s3-request-presigner');

const s3 = new S3Client({ region: process.env.AWS_REGION });

exports.handler = async (event) => {
  if (event.httpMethod !== 'POST') {
    return { statusCode: 405, body: 'Method Not Allowed' };
  }

  const { filename, contentType, size } = JSON.parse(event.body);

  // サーバー側でのバリデーション
  const MAX_SIZE = 100 * 1024 * 1024; // 100MB
  if (size > MAX_SIZE) {
    return { statusCode: 413, body: JSON.stringify({ error: 'ファイルが大きすぎます。' }) };
  }

  const allowedTypes = ['image/jpeg', 'image/png', 'image/webp', 'application/pdf'];
  if (!allowedTypes.includes(contentType)) {
    return { statusCode: 415, body: JSON.stringify({ error: '許可されていない形式です。' }) };
  }

  const key = `uploads/${Date.now()}-${filename}`;

  const command = new PutObjectCommand({
    Bucket: process.env.AWS_S3_BUCKET,
    Key: key,
    ContentType: contentType,
    ContentLength: size,
  });

  // 署名付き URL を生成(このリクエストはファイル本体を含まないため制限外)
  const presignedUrl = await getSignedUrl(s3, command, { expiresIn: 900 }); // 15分

  return {
    statusCode: 200,
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ presignedUrl, key }),
  };
};
// クライアント側:Netlify Function から Presigned URL を取得して S3 に直接アップロード
async function uploadFile(file) {
  // 1. Netlify Function から Presigned URL を取得
  const res = await fetch('/.netlify/functions/get-upload-url', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      filename: file.name,
      contentType: file.type,
      size: file.size,
    }),
  });

  const { presignedUrl, key } = await res.json();

  // 2. S3 に直接 PUT(Netlify Function を経由しないため容量制限なし)
  const uploadRes = await fetch(presignedUrl, {
    method: 'PUT',
    headers: { 'Content-Type': file.type },
    body: file,
  });

  if (!uploadRes.ok) throw new Error('アップロードに失敗しました');

  return key;
}

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 über <code>Netlify Functions</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 6MB
  • → <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/vercel-upload-config/" class="text-primary-600 dark:text-primary-400 hover:underline">Vercel-Datei-Upload-Konfiguration und Limits | Vercel Blob, API-Limits und Lösungsansätze</a>
  • → <a href="/ja/blog/render-upload-config/" class="text-primary-600 dark:text-primary-400 hover:underline">Konfiguration von PHP/Node.js-Datei-Upload auf Render | Datenträger, Umgebungsvariablen und S3-Integration</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>