Größenlimits für Bild-Uploads in Node.js
Ein Foto direkt vom Smartphone ist oft größer als das Limit, das Sie im Sinn hatten. In einem Node.js-Setup können bis zu fünf Stellen es ablehnen, jede mit einer anderen Antwort. Diese Seite geht sie in der Reihenfolge durch, in der ein Request auf sie trifft, zeigt, wie das Express-Beispiel das Limit von multer in ein sauberes JSON-413 umwandelt, und behandelt ein Verhalten von multer, das bestimmt, wie viel von einem riesigen Upload Ihr Server trotzdem liest.
Die Ebenen, der Reihe nach
- Der Galerie-Dialog. Mit
maxUploadFileSize: 4000000in den Editor-Optionen lehnt der Dialog eine größere Datei ab, bevor er sie sendet. Eine Datei mit 4.5 MB ergab „big.jpg is 4.3 MB. The maximum allowed size is 3.8 MB.“ (der Dialog rechnet in binären Megabytes), und es wurde kein Request gesendet. Das ist eine Komfortfunktion. Jeder kann die URL auch ohne den Dialog aufrufen. - Ein Reverse Proxy, falls Sie einen haben. Laut nginx-Dokumentation ist der Standardwert von
client_max_body_size1m, sodass hinter einem nginx mit Standardeinstellungen ein 2-MB-Foto Node nie erreicht. nginx antwortet mit seiner eigenen HTML-Seite für 413, die der Dialog als fehlgeschlagenen Upload meldet. - multers
limits, die Sie selbst setzen. Die eigenen Standardwerte fürfileSize,files,fieldsundpartssindInfinity, und mit Memory Storage bedeutet das die gesamte Datei im Speicher. - Die Fehler-Middleware, die den Fehler von multer in das JSON umwandelt, das der Browser erwartet.
- Die eigene Prüfung des Beispiels in
saveImage(), mit denselben 4,000,000 Bytes, die nur greift, falls das multer-Limit jemals erhöht wird.
Damit die Meldung des Beispiels diejenige ist, die die Benutzer sehen, muss jede frühere Ebene etwas mehr als 4,000,000 Bytes zulassen. Die Multipart-Kodierung fügt rund um die Datei einige hundert Bytes hinzu.
Das Limit von multer
// multer is the multipart parser most Express apps already use. Memory storage keeps the
// file in req.file.buffer, so its bytes can be checked before anything touches the disk;
// the limits keep that buffer small. multer allows exactly fileSize bytes and fails with
// LIMIT_FILE_SIZE above that (turned into a 413 in the error handler below).
const parseForm = multer({
storage: multer.memoryStorage(),
limits: { fileSize: MAX_BYTES, files: 1, fields: 10, fieldSize: 1024, parts: 12 },
defParamCharset: "utf8", // file names are sent as UTF-8; multer's default is latin1
});fileSize ist die größte erlaubte Größe, nicht die erste abgelehnte. Wir haben eine Datei mit genau 4,000,000 Bytes gesendet, und sie wurde gespeichert. 4,000,001 Bytes wurden abgelehnt. Die anderen Limits gibt es, weil Memory Storage alles behält: eine Datei, zehn Textfelder mit höchstens 1,024 Bytes, insgesamt zwölf Teile. multers Standardwert für fieldSize ist 1 MB pro Textfeld.
Vom MulterError zum 413
Wenn eine Datei fileSize überschreitet, ruft multer next(err) mit einem MulterError auf. Sein code ist "LIMIT_FILE_SIZE", seine message ist "File too large" und sein field ist "file". Ohne Fehlerhandler erhielten wir das Standardverhalten von Express: Status 500 mit einer HTML-Seite, und sofern NODE_ENV=production nicht gesetzt ist, enthält diese Seite den Stacktrace. Die Fehler-Middleware des Beispiels prüft den Code:
// Express 5 sends errors thrown in async handlers here, so the routes need no try/catch.
app.use((err, req, res, next) => {
if (err instanceof GalleryError) {
return res.status(err.status).json({ error: err.message });
}
if (err instanceof multer.MulterError) {
const tooBig = err.code === "LIMIT_FILE_SIZE";
return res
.status(tooBig ? 413 : 400)
.json({ error: tooBig ? "The image is larger than 4,000,000 bytes." : "Invalid upload: " + err.message });
}
console.error(err);
res.status(500).json({ error: "Server error." });
});Jeder andere Fehler von multer (LIMIT_FILE_COUNT bei einer zweiten Datei, LIMIT_UNEXPECTED_FILE bei einer Datei unter einem anderen Feldnamen) wird zu einem 400 mit der Meldung von multer. Prüfen Sie err.code, nicht den Meldungstext.
multer liest zuerst den Rest des Bodys
multer sendet das 413 nicht in dem Moment, in dem das Limit überschritten wird. Bei einem Fehler liest und verwirft es den Rest des Requests, bevor es next(err) aufruft. Ein Kommentar im Quellcode von multer nennt den Grund: Wird die Verbindung geschlossen, während der Client noch sendet, verursacht das EPIPE-Fehler im Client. Wir haben es gemessen: Bei einem 20-MB-Upload ohne Content-Length hatte der Client alle 20,000,000 Bytes gesendet, bevor das 413 zurückkam. Das Next.js-Beispiel, das beim Limit aufhört zu lesen, antwortete nach etwa 4.9 MB.
Der Speicher ist sicher, weil die zusätzlichen Bytes verworfen werden. Bandbreite und Zeit nicht: Ein Upload mit 1 GB wird bis zum Ende gelesen. Wenn das wichtig ist, lehnen Sie große Bodys schon vor Node am Reverse Proxy ab oder prüfen Sie req.headers["content-length"] in einer Middleware vor multer. Eine Prüfung von Content-Length allein deckt keine Chunked Requests ab, die keine Länge mitführen.
Testen
Starten Sie das Beispielprojekt zu dieser Anleitung mit npm start, erzeugen Sie eine Datei über dem Limit und senden Sie sie:
head -c 4500000 /dev/zero > big.jpg
curl -F "file=@big.jpg" http://localhost:5101/rte/upload
{"error":"The image is larger than 4,000,000 bytes."}Der Status ist 413. Die Datei besteht nur aus Nullen und würde daher auch an der JPEG-Signaturprüfung scheitern, aber das Limit von multer greift zuerst. Um die eigene Prüfung des Dialogs zu sehen, wählen Sie über die Upload-Schaltfläche der Galerie eine Datei mit mehr als 4,000,000 Bytes. Der Dialog zeigt die Größenmeldung an und sendet nichts.
Dieselbe Anleitung für andere Plattformen
Next.js · PHP · ASP.NET Core · MVC 5 · Web Forms