Como fazer upload de arquivos com Netlify Functions | Limites, Large Media e Soluções Alternativas
Ao implementar upload de arquivo em Netlify Functions (função serverless baseada em AWS Lambda), o corpo da requisição tem um limite. Muitos desenvolvedores enfrentam problemas por desconhecer esse limite padrão de 6MB. Este artigo explica o limite de tamanho do corpo em Netlify Functions, configuração via <code>netlify.toml</code>, métodos de parse para <code>multipart/form-data</code> e alternativas para lidar com arquivos que excedem o limite.
Limite de tamanho do corpo em Netlify Functions
Netlify Functions é baseada em AWS Lambda e possui um limite fixo de tamanho para o corpo da requisição.
| Tipos de Function | Limite de tamanho do corpo | Possibilidade de alteração | Observações |
|---|---|---|---|
| Netlify Functions (síncrono) | 6 MB | Não permitido | Limitações do AWS Lambda |
| Netlify Functions(Background) | 6 MB | Não permitido | O tempo de processamento é no máximo 15 minutos |
| Netlify Edge Functions | Com limitações | Não permitido | Runtime baseado em Deno |
Enviar requisições maiores que 6MB para Netlify Functions resulta em HTTP 413 ou timeout da função. Embora seja ligeiramente maior que Vercel (4.5MB), ainda não é possível receber diretamente arquivos grandes como imagens e vídeos.
Configuração de netlify.toml
Em <code>netlify.toml</code> você pode configurar o runtime, timeout, memória, redirecionamentos, headers e muito mais para Functions. O tamanho do corpo em si não pode ser alterado, mas é importante entender as configurações relacionadas.
# 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"
Implementação de análise de multipart/form-data
Em Netlify Functions, o corpo da requisição pode ser codificado em Base64 por padrão. Explicamos como fazer parse de dados multipart usando <code>busboy</code> ou <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 (Depreciado) e Alternativas
Netlify Large Media era um serviço baseado em Git LFS para gerenciar arquivos grandes, mas atualmente novas inscrições são restritas e é recomendado migrar para alternativas.
# Netlify Large Media(非推奨・新規利用不可)
# .lfsconfig
[lfs]
url = https://large-media.netlify.com/<repo-id>
a configuração recomendada como alternativa é a seguinte.
// 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;
}
Arquivo de teste disponível para usar neste artigo (gratuito)
- → <a href="/ja/files/images/png/1mb/" class="text-primary-600 dark:text-primary-400 hover:underline">Imagem PNG de teste (1MB)</a> — Para verificação de operação de upload via <code>Netlify Functions</code>
- → <a href="/ja/files/pdf/1mb/" class="text-primary-600 dark:text-primary-400 hover:underline">Arquivo de teste PDF (1MB)</a> — Para teste de upload de arquivo dentro do limite de 6MB
- → <a href="/ja/files/zip/1mb/" class="text-primary-600 dark:text-primary-400 hover:underline">Arquivo de teste ZIP (1MB)</a> — Para teste de upload direto no S3 via Presigned URL
Artigos relacionados
- → <a href="/ja/blog/vercel-upload-config/" class="text-primary-600 dark:text-primary-400 hover:underline">Configuração e limites de upload de arquivos do Vercel | Vercel Blob, limitações de API e soluções alternativas</a>
- → <a href="/ja/blog/render-upload-config/" class="text-primary-600 dark:text-primary-400 hover:underline">Como configurar upload de arquivos PHP/Node.js no Render | Disco, variáveis de ambiente e integração S3</a>
- → <a href="/ja/blog/s3-upload-limit/" class="text-primary-600 dark:text-primary-400 hover:underline">Resumo dos limites de upload de arquivos do AWS S3 e CloudFront</a>