12k
All articles

Modernes File Handling in Node.js

Moderne Dateioperationen in Node.js mit fs/promises, Streams, File Handles, Fehlercodes, Parallelität und Schutz vor Path Traversal.

OpenReplay Team
OpenReplay Team
Modernes File Handling in Node.js

In modernem Node.js ist das promise-basierte Modul node:fs/promises mit async/await die Standardwahl für Datei-I/O. Greifen Sie auf Streams zurück (pipeline aus node:stream/promises), wenn Dateien groß oder von unbekannter Größe sind, und auf File Handles (open/read/close), wenn Sie byteweise positionsgenauen Zugriff benötigen. Das fs-Modul liefert nach wie vor drei parallele APIs — synchron, Callback und Promise — und die meisten älteren Tutorials beginnen mit der Callback-API und CommonJS require(). Diese Herangehensweise ist veraltet: Für neuen Code auf einer aktuellen Laufzeitumgebung ist node:fs/promises mit ES-Modul-import der richtige Ausgangspunkt.

Dieser Artikel zeigt, wie Dateien mit dieser modernen API gelesen, geschrieben und verarbeitet werden: Encoding-Verhalten, Überschreibsemantik, Fehlerbehandlung über code, Nebenläufigkeit mit Promise.all versus Promise.allSettled, die Speichergrenzen, an denen readFile versagt, Streams und File Handles für große Datenmengen, Verzeichnisoperationen sowie die Absicherung gegen Path-Traversal-Angriffe. Die Beispiele richten sich an Node 24, die Active-LTS-Version ab 2026 (Node.js Release-Zeitplan). Alle gezeigten Features liegen deutlich unterhalb dieser Baseline, sodass der Code unverändert auf Node 24 läuft. Sämtliche Beispiele verwenden ESM import mit dem node:-Präfix und Top-Level-await.

Wichtige Erkenntnisse

  • Verwenden Sie node:fs/promises mit async/await als Standard für Datei-I/O. Synchrone Methoden wie readFileSync blockieren den Event Loop und gehören ausschließlich in einmalig ausgeführte CLI-Skripte, niemals in Server.
  • readFile puffert die gesamte Datei im Arbeitsspeicher und wirft ERR_FS_FILE_TOO_LARGE für jede Datei, die größer als 2 GiB ist — eine feste libuv-I/O-Grenze, unabhängig von den Buffer- und String-Längenbeschränkungen. Ab einigen hundert MiB sollten Sie daher bereits auf Streaming umsteigen.
  • pipeline() aus node:stream/promises (stabil seit Node 15) verbindet Streams und übernimmt automatisch die Fehlerweiterleitung und das Aufräumen.
  • Verwenden Sie Promise.all, wenn jede Datei erfolgreich gelesen werden muss; verwenden Sie Promise.allSettled, wenn partielle Fehler akzeptabel sind und Sie das Gelesene weiterverarbeiten möchten.
  • Übergeben Sie niemals nicht validierte Benutzereingaben an fs-Funktionen: Lösen Sie den Pfad mit path.resolve auf und prüfen Sie, ob er innerhalb Ihres vorgesehenen Basisverzeichnisses bleibt, um Path-Traversal-Angriffe zu verhindern.

Die drei fs-APIs und warum fs/promises der Standard ist

Node.js stellt dieselben Dateioperationen über drei APIs bereit: synchron (readFileSync), Callback (readFile(path, cb)) und Promise (node:fs/promises). Die Promise-API wurde in Node 14.0.0 stabil (Node.js 14 Release Notes) und ist der moderne Standard, da sie sich sauber mit async/await integriert und den Event Loop niemals blockiert. Die Callback-API ist Legacy — sie entstand vor Promises und führt zu tief verschachteltem Code — und die synchrone API blockiert den Event Loop für die gesamte Dauer der I/O-Operation. Verwenden Sie die Promise-API für neuen Code.

import { readFile } from 'node:fs/promises'

const data = await readFile('config.json', 'utf8')
console.log(data.length)

Das node:-Präfix kennzeichnet den Import als Node.js-Builtin und kann nicht durch ein npm-Paket gleichen Namens überschattet werden; es identifiziert das Modul explizit als Node.js-Kernmodul. Dieses Beispiel verwendet außerdem Top-Level-await, das seit Node 14.8.0 ohne Flag in ES-Modulen verfügbar ist (Node.js 14.8.0 Release Notes). Sie müssen Dateilesevorgänge auf Modulebene nicht mehr in ein async-IIFE einwickeln.

Welche API sollten Sie verwenden?

Die Entscheidung hängt von der Dateigröße, der Art des benötigten Zugriffs und davon ab, ob der Prozess nebenläufige Arbeit verarbeitet. Verwenden Sie diese Tabelle als Entscheidungsregel:

SzenarioEmpfohlene APIBegründung
Kleine Dateien, grob unter 100 MBnode:fs/promises readFile/writeFileEinfachste API; die gesamte Datei passt problemlos in den Arbeitsspeicher, und ein einzelnes await erledigt die Aufgabe
Große Dateien oder Dateien unbekannter GrößeStreams: createReadStream/createWriteStream mit pipelineKonstanter Speicherverbrauch unabhängig von der Größe; die 2-GiB-Grenze von readFile wird vollständig umgangen
Byteweiser oder positionsgenaue Zugriff (Lesen/Schreiben an bestimmten Offsets)File Handles: open/read/closeNur die Handle-API ermöglicht das Lesen oder Schreiben bestimmter Bytebereiche an gewählten Positionen
Einmalige CLI-Skripte und Build-Tools (keine Nebenläufigkeit)Synchrone Methoden sind akzeptabel: readFileSync/writeFileSyncDas Blockieren des Event Loops ist harmlos, wenn nichts anderes ausgeführt wird
HTTP-Server oder jeglicher nebenläufiger CodeAusschließlich asynchrone Promise-API — niemals synchronEin blockierter Event Loop bremst alle ausstehenden Anfragen gleichzeitig aus

Der Richtwert von ~100 MB ist eine praktische Faustregel dafür, wann auf Streams umgestiegen werden sollte, keine harte Grenze; der tatsächliche Fehlerpunkt für readFile ist die 2-GiB-libuv-Grenze. Wenn Sie zwischen fs/promises und Streams unentschieden sind, ist Streaming die sicherere Standardwahl für alles, dessen Größe Sie nicht kontrollieren.

Dateien lesen mit fs/promises

readFile() lädt die gesamte Datei in den Arbeitsspeicher und gibt entweder einen String oder einen Buffer zurück. Übergeben Sie ein Encoding wie 'utf8', um einen dekodierten String zu erhalten; lassen Sie das Encoding weg, um rohe Bytes als Buffer zu erhalten. Für Konfigurations- und Datendateien lesen Sie als String und parsen:

import { readFile } from 'node:fs/promises'

const raw = await readFile('config.json', 'utf8')
const config = JSON.parse(raw)
console.log(config.port)

Ohne Encoding ist der Rückgabewert ein Buffer — die richtige Wahl für Bilder, Audio oder beliebige Nicht-Text-Daten:

import { readFile } from 'node:fs/promises'

const bytes = await readFile('logo.png') // Buffer
console.log(bytes.length, 'bytes')

JSON.parse wirft bei fehlerhafter Eingabe einen SyntaxError, daher behandelt ein try/catch um den Parse-Vorgang sowohl I/O-Fehler als auch ungültiges JSON. Die vollständige Options-Oberfläche finden Sie in der fs.readFile-Dokumentation.

Dateien schreiben mit fs/promises

writeFile() erstellt die Datei, falls sie nicht existiert, oder überschreibt sie vollständig, falls sie existiert — aus Sicht des Aufrufers ersetzt ein einzelnes await den gesamten Dateiinhalt. Um Inhalte an eine Datei anzuhängen, anstatt sie zu ersetzen, verwenden Sie appendFile(), das die Datei auch dann erstellt, wenn sie fehlt. Um eine Datei auf eine feste Länge zu kürzen, verwenden Sie truncate().

import { writeFile, appendFile } from 'node:fs/promises'

const user = { name: 'Ada', email: 'ada@example.com' }

// Erstellt user.json oder ersetzt den Inhalt vollständig
await writeFile('user.json', JSON.stringify(user, null, 2), 'utf8')

// Fügt eine Zeile hinzu, ohne die Datei neu zu schreiben; erstellt sie, falls nicht vorhanden
await appendFile('events.log', `${new Date().toISOString()} user-created\n`, 'utf8')

truncate(path, n) behält die ersten n Bytes der Datei und verwirft den Rest — das Argument ist die Anzahl der zu behaltenden Bytes, nicht die Anzahl der zu entfernenden Bytes, was dem entgegengesetzt ist, was der Name vermuten lässt. Das Kürzen von 1234567890 auf 5 hinterlässt 12345:

import { writeFile, truncate, readFile } from 'node:fs/promises'

await writeFile('data.txt', '1234567890')
await truncate('data.txt', 5)
console.log(await readFile('data.txt', 'utf8')) // '12345'

Fehlerbehandlung: Abfangen über error.code

Dateioperationen schlagen mit Systemfehlern fehl, die eine code-Eigenschaft tragen; verzweigen Sie über error.code statt Fehlermeldungen zu parsen. Die am häufigsten behandelten Codes sind in der Node.js-Referenz für häufige Systemfehler dokumentiert:

error.codeBedeutung
ENOENTDatei oder Verzeichnis existiert nicht
EACCESZugriff verweigert
EISDIRVersucht, ein Verzeichnis als Datei zu lesen
ENOSPCKein Speicherplatz mehr auf dem Gerät (Festplatte voll)
EMFILEZu viele offene File-Deskriptoren

Ein einzelnes try/catch deckt sowohl den I/O-Fehler als auch bei JSON den Parse-Fehler ab:

import { readFile } from 'node:fs/promises'

try {
  const config = JSON.parse(await readFile('config.json', 'utf8'))
  console.log('loaded', config)
} catch (error) {
  if (error.code === 'ENOENT') {
    console.error('config.json fehlt; verwende Standardwerte')
  } else if (error.code === 'EACCES') {
    console.error('Keine Berechtigung zum Lesen von config.json')
  } else {
    throw error // Enthält SyntaxError von JSON.parse und unerwartete Codes
  }
}

Werfen Sie Codes, die Sie nicht explizit behandeln, erneut, anstatt sie zu schlucken — still abgefangene ENOSPC- oder EMFILE-Fehler sind ein häufiges Produktionsversagen, das die eigentliche Ursache verschleiert.

Sync vs. Async: Wann synchrone Methoden akzeptabel sind

Synchrone fs-Methoden (readFileSync, writeFileSync) blockieren den Node.js-Event-Loop für die gesamte Dauer der I/O-Operation — in der Zwischenzeit wird kein anderes JavaScript ausgeführt. Sie sind in einmalig ausgeführten CLI-Skripten und Build-Tools akzeptabel, wo keine Nebenläufigkeit besteht, aber niemals in HTTP-Servern oder in Code, der nebenläufige Anfragen verarbeitet, da ein blockierter Event Loop alle ausstehenden Anfragen gleichzeitig zum Stillstand bringt (Node.js Event-Loop-Leitfaden).

// Akzeptabel: ein einmaliges Skript, das ausgeführt wird und beendet
import { readFileSync } from 'node:fs'

const pkg = JSON.parse(readFileSync('package.json', 'utf8'))
console.log(pkg.version)

Die Entscheidungsregel: Wenn der Prozess mehr als eine Sache gleichzeitig bedient, verwenden Sie die asynchrone Promise-API.

Nebenläufigkeit: Promise.all vs. Promise.allSettled

Verwenden Sie Promise.all, wenn jeder Dateilesvorgang erfolgreich sein muss und ein einzelner Fehler den gesamten Batch abbrechen soll; verwenden Sie Promise.allSettled, wenn partielle Fehler akzeptabel sind und Sie das Gelesene weiterverarbeiten möchten. Promise.allSettled löst immer auf und gibt ein Array zurück, in dem jeder Eintrag entweder { status: 'fulfilled', value } oder { status: 'rejected', reason } ist.

Das Abbilden von Dateinamen auf Promises ohne await innerhalb der Schleife führt die Lesevorgänge nebenläufig aus:

import { readFile } from 'node:fs/promises'

const files = ['a.json', 'b.json', 'c.json']

// Alles-oder-nichts: eine fehlende Datei lehnt den gesamten Batch ab
const all = await Promise.all(
  files.map((f) => readFile(f, 'utf8').then(JSON.parse)),
)

Wenn Sie lieber laden, was vorhanden ist, und den Rest melden möchten, untersuchen Sie die abgeschlossenen Ergebnisse:

import { readFile } from 'node:fs/promises'

const files = ['a.json', 'b.json', 'missing.json']

const results = await Promise.allSettled(
  files.map((f) => readFile(f, 'utf8')),
)

const loaded = results.filter((r) => r.status === 'fulfilled').map((r) => r.value)
const failed = results
  .filter((r) => r.status === 'rejected')
  .map((r) => r.reason.code) // z.B. 'ENOENT'

console.log(`geladen ${loaded.length}, fehlgeschlagen:`, failed)

fs-Promises und große Dateien: Die Speichergrenzen, an denen readFile versagt

readFile puffert die gesamte Datei im Arbeitsspeicher und wirft ERR_FS_FILE_TOO_LARGE für jede Datei, die größer als 2 GiB ist — eine feste libuv-I/O-Grenze, nicht die Buffer-Größengrenze (node#55864, ERR_FS_FILE_TOO_LARGE). Lange vor dieser harten Grenze verursacht das Laden von Hunderten von Megabytes in einen einzelnen Buffer Speicherdruck und langsame Antwortzeiten — ab einigen hundert MiB sollten Sie daher bereits auf Streaming umsteigen.

Drei unterschiedliche Obergrenzen werden leicht verwechselt, und die meisten Tutorials vermischen sie:

GrenzeWert (64-Bit)Gilt für
libuv-Dateilesegrenze2 GiBreadFile für jede Datei; wirft ERR_FS_FILE_TOO_LARGE
buffer.constants.MAX_STRING_LENGTH536.870.888 Bytes (~512 MiB)Strings, d.h. readFile mit einem Encoding
buffer.constants.MAX_LENGTH2⁵³−1 Bytes (~8 PiB)Maximale Buffer-Allokation

Wenn Sie readFile ein Encoding übergeben (was einen String zurückgibt), ist die maßgebliche Obergrenze buffer.constants.MAX_STRING_LENGTH — 536.870.888 Bytes auf 64-Bit-Plattformen, gesenkt von etwa 1 GB in Node 14.4.0 (node#33960). Das Überschreiten dieser Grenze wirft einen „Cannot create a string longer than…”-Fehler, nicht ERR_FS_FILE_TOO_LARGE. Der Wert buffer.constants.MAX_LENGTH ist die maximale Buffer-Allokation und eine wiederum separate Grenze; er begrenzt readFile nicht, das unabhängig davon bei 2 GiB versagt. Die praktische Schlussfolgerung: Leiten Sie das Verhalten von readFile nicht von der Buffer-Grenze ab — die 2-GiB-libuv-Grenze ist diejenige, die zuerst greift.

Streams: createReadStream, createWriteStream und pipeline

Streams verarbeiten Dateidaten in Chunks, anstatt die gesamte Datei im Arbeitsspeicher zu puffern, was sie zum richtigen Werkzeug für große oder größenmäßig unbekannte Dateien macht. createReadStream und createWriteStream erzeugen lesbare und schreibbare Streams; verbinden Sie diese mit pipeline aus node:stream/promises, stabil seit Node 15. pipeline() verbindet einen lesbaren mit einem schreibbaren Stream und übernimmt automatisch die Fehlerweiterleitung und das Aufräumen der Streams. Während pipe() bereits Backpressure verwaltet, bietet pipeline() eine sicherere Fehlerbehandlung und Bereinigung.

import { createReadStream, createWriteStream } from 'node:fs'
import { pipeline } from 'node:stream/promises'

// Kopiert eine Datei beliebiger Größe mit konstantem Speicherverbrauch
await pipeline(
  createReadStream('huge-input.log'),
  createWriteStream('huge-output.log'),
)

Da pipeline() ein Promise zurückgibt, das bei jedem Stream-Fehler abgelehnt wird und die Streams bei einem Fehler zerstört, genügt ein einzelnes try/catch — es müssen keine 'error'-Listener manuell verdrahtet werden.

Backpressure ist der Mechanismus, der verhindert, dass ein schneller Leser einen langsamen Schreiber überwältigt: Wenn der interne Puffer des Writables voll ist, pausiert der Readable, bis er geleert wird. Der Pufferschwellenwert ist der highWaterMark. Datei-Read-Streams haben standardmäßig einen highWaterMark von 64 KiB, im Gegensatz zu den 16 KiB eines generischen stream.Readable; die fs-Dokumentation nennt den 64-KiB-Wert explizit als Standard für Datei-Streams. Passen Sie ihn an, wenn Profiling zeigt, dass es hilft:

import { createReadStream } from 'node:fs'

const stream = createReadStream('data.bin', { highWaterMark: 128 * 1024 })

Für zeilenbasierte Formate wie CSV leiten Sie den Read-Stream durch einen Parser wie csv-parser, anstatt manuell zu parsen.

File Handles: Byteweiser und positionsgenauer Zugriff

Verwenden Sie ein File Handle von open(), wenn Sie bestimmte Bytebereiche an bestimmten Offsets lesen oder schreiben müssen — etwas, das readFile und Streams nicht ermöglichen. Ein FileHandle ist eine Low-Level-Ressource: Sie verwalten Buffer und Position selbst und müssen das Handle immer mit close() in einem finally-Block freigeben, sonst entsteht ein Deskriptor-Leak (und genug Leaks erzeugen EMFILE).

import { open } from 'node:fs/promises'

async function readInChunks(path) {
  let handle
  try {
    handle = await open(path, 'r')
    const chunkSize = 64 * 1024
    const buffer = Buffer.alloc(chunkSize)
    let position = 0
    while (true) {
      const { bytesRead } = await handle.read(buffer, 0, chunkSize, position)
      if (bytesRead === 0) break
      // buffer.subarray(0, bytesRead) verarbeiten
      position += bytesRead
    }
  } finally {
    await handle?.close()
  }
}

handle.read(buffer, offset, length, position) füllt buffer ab einer gegebenen Datei-position und meldet bytesRead; das manuelle Nachverfolgen von position ermöglicht den positionsgenauen Zugriff. Für die meisten Kopier- und Transformationsaufgaben sind Streams das bessere Werkzeug — File Handles sind die Ausführlichkeit nur dann wert, wenn Sie wirklich byteweisen Zugriff benötigen.

AbortSignal wird von readFile und fs.watch unterstützt, sodass ein langsamer Lesevorgang abgebrochen werden kann — beispielsweise wenn eine Anfrage das Timeout überschreitet. Ein abgebrochener Lesevorgang wird mit einem Fehler abgelehnt, dessen name 'AbortError' ist:

import { readFile } from 'node:fs/promises'

const controller = new AbortController()
setTimeout(() => controller.abort(), 1000)

try {
  await readFile('slow-source.bin', { signal: controller.signal })
} catch (error) {
  if (error.name === 'AbortError') console.error('Lesevorgang abgebrochen')
  else throw error
}

Verzeichnisse und Pfade

Erstellen Sie verschachtelte Verzeichnisse mit mkdir({ recursive: true }) (kein Fehler, wenn sie bereits existieren, ähnlich wie mkdir -p), listen Sie Einträge mit readdir({ withFileTypes: true }) auf, um Dirent-Objekte zu erhalten, und entfernen Sie einen Verzeichnisbaum mit rm({ recursive: true }) — alles aus demselben node:fs/promises-Modul. Erstellen Sie Pfade mit path.join, damit Trennzeichen betriebssystemübergreifend korrekt sind.

import { mkdir, readdir, rm } from 'node:fs/promises'
import { join } from 'node:path'

await mkdir('output/reports', { recursive: true })

const entries = await readdir('output', { withFileTypes: true })
for (const entry of entries) {
  const full = join('output', entry.name)
  if (entry.isFile()) console.log('Datei:', full)
  else if (entry.isDirectory()) console.log('Verz.:', full)
}

await rm('output/reports', { recursive: true, force: true })

Iterieren Sie das Array mit for...of, nicht mit for...infor...in über ein Array liefert String-Indizes, keine Werte, ein häufiger Fehler in älteren Anleitungen. rm (mit recursive: true) ist der aktuelle Weg zum Löschen von Verzeichnisbäumen; rmdir mit der rekursiven Option ist veraltet.

Um Dateien relativ zum aktuellen Modul statt zum Arbeitsverzeichnis des Prozesses zu referenzieren, verwenden Sie import.meta.dirname. Verfügbar seit Node 20.11.0 (import.meta.dirname), bietet es ES-Modulen dieselbe Verzeichnisreferenz, die __dirname in CommonJS bereitstellte:

import { readFile } from 'node:fs/promises'
import { join } from 'node:path'

const config = await readFile(join(import.meta.dirname, 'config.json'), 'utf8')

Berechtigungs- und Eigentümerfunktionen (chmod, chown) sowie Link-Funktionen (symlink, link) sind ebenfalls in der Promise-API vorhanden, gelten jedoch für Unix-ähnliche Systeme und verhalten sich unter Windows unerwartet oder werfen Fehler; behandeln Sie sie als plattformspezifisch.

Sicherheit: Path-Traversal mit Benutzereingaben verhindern

Übergeben Sie niemals vom Benutzer stammende Strings direkt an readFile, writeFile oder eine andere fs-Funktion. Normalisieren Sie den Pfad mit path.resolve und prüfen Sie, ob er mit Ihrem vorgesehenen Basisverzeichnis beginnt, bevor Sie fortfahren — eine rohe Eingabe wie ../../../etc/passwd traversiert andernfalls aus jedem relativen Pfad heraus, den Sie einschränken wollten. Ein naives join(baseDir, userInput) schützt Sie nicht, da ..-Segmente nach oben aufgelöst werden.

import { resolve, sep } from 'node:path'
import { readFile } from 'node:fs/promises'

const baseDir = resolve('uploads')

async function readUserFile(userPath) {
  const target = resolve(baseDir, userPath)
  if (target !== baseDir && !target.startsWith(baseDir + sep)) {
    throw new Error('Path-Traversal blockiert')
  }
  return readFile(target, 'utf8')
}

Durch das vorherige Auflösen und anschließende Prüfen des Präfixes gegen baseDir + sep wird die Anfrage unabhängig davon, wie viele ..-Segmente die Eingabe enthält, innerhalb des erlaubten Verzeichnisses gehalten. Der + sep-Schutz verhindert, dass ein Geschwisterverzeichnis wie uploads-private eine bloße startsWith('uploads')-Prüfung besteht.

Fazit

Beginnen Sie für neuen Node.js-Code mit node:fs/promises und async/await, steigen Sie auf Streams mit pipeline() um, sobald Dateien einige hundert Megabytes überschreiten oder eine unbekannte Größe haben, und wechseln Sie nur dann zu File Handles, wenn Sie byteweisen positionsgenauen Zugriff benötigen. Ordnen Sie jede Operation ihrer tatsächlichen Einschränkung zu — Speicherobergrenze, Nebenläufigkeit und nicht vertrauenswürdige Eingaben sind die drei, die am häufigsten zu Problemen führen — und greifen Sie auf die offizielle fs-Dokumentation zurück, wenn Sie die vollständige Options-Oberfläche benötigen. Der nächste Schritt besteht darin, vorhandenen Callback- oder Sync-fs-Code in Ihren Servern zu prüfen und auf die Promise-API zu migrieren.

FAQs

Was ist der Unterschied zwischen fs und fs/promises in Node.js?

Beide beziehen sich auf dieselben Dateioperationen, stellen jedoch unterschiedliche Schnittstellen bereit. Das Basismodul node:fs bietet synchrone Methoden (readFileSync) und Callback-basierte asynchrone Methoden (readFile mit einem Callback-Argument), während node:fs/promises Promise-zurückgebende Versionen derselben Operationen bereitstellt, die mit async und await funktionieren. Die Promise-API wurde in Node 14.0.0 stabil und ist der empfohlene Standard für neuen Code, da sie Callback-Verschachtelung vermeidet und den Event Loop niemals blockiert.

Kann ich require mit fs/promises verwenden, oder benötige ich ES-Module?

Sie können beides verwenden. In ES-Modulen schreiben Sie import { readFile } from 'node:fs/promises'. In CommonJS schreiben Sie const { readFile } = require('node:fs/promises'). Die Promise-API selbst erfordert kein ESM; nur Top-Level-await und import.meta.dirname benötigen einen ES-Modul-Kontext. Das node:-Präfix funktioniert in beiden Formaten und kennzeichnet den Import als Node.js-Builtin, das kein npm-Paket überschatten kann.

Warum wirft readFile ERR_FS_FILE_TOO_LARGE, bevor die Buffer-Größengrenze erreicht wird?

ERR_FS_FILE_TOO_LARGE ist eine feste 2-GiB-libuv-I/O-Grenze für eine einzelne Leseoperation, unabhängig von der Buffer-Allokationsgrenze. Eine Datei knapp über 2 GiB schlägt fehl, obwohl buffer.constants.MAX_LENGTH weit höher ist, da der zugrundeliegende Lese-Syscall-Pfad die 2-GiB-Grenze unabhängig davon durchsetzt, wie viel Speicher ein Buffer halten könnte. Um größere Dateien zu verarbeiten, verwenden Sie Streams mit pipeline anstatt die gesamte Datei zu puffern.

Wie lese ich einen Teil einer Datei an einem bestimmten Byte-Offset?

Öffnen Sie die Datei mit open() aus node:fs/promises, um ein FileHandle zu erhalten, und rufen Sie dann handle.read(buffer, offset, length, position) auf, das buffer ab der angegebenen Dateiposition füllt und bytesRead meldet. Verfolgen Sie position manuell über mehrere Lesevorgänge hinweg, um durch die Datei zu navigieren. Geben Sie das Handle immer mit handle.close() in einem finally-Block frei, sonst entsteht ein Deskriptor-Leak, und genug Leaks erzeugen EMFILE. Einfaches readFile und Streams ermöglichen keinen positionsgenauen Zugriff.

DevTools for the frontend

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers.

Star on GitHub12k

We use cookies to improve your experience. By using our site, you accept cookies.