EMZETT.
Login

FileInputStream

Kurz: Ein Byte-Stream zum Lesen roher Binärdaten aus einer Datei — z. B. für Bilder, PDFs oder andere Nicht-Textformate.

Genauer: Für reinen Textinhalt ist ein Reader (z. B. über FileReader/BufferedReader) meist die bessere Wahl, da der dabei automatisch die Zeichenkodierung berücksichtigt — FileInputStream liefert nur rohe Bytes ohne Kodierungswissen.

Im Detail

try (FileInputStream fis = new FileInputStream("bild.jpg");
     FileOutputStream fos = new FileOutputStream("kopie.jpg")) {
 
    byte[] puffer = new byte[4096]; // blockweise lesen, nicht Byte für Byte
    int gelesen;
    while ((gelesen = fis.read(puffer)) != -1) {
        fos.write(puffer, 0, gelesen); // nur die tatsächlich gelesenen Bytes schreiben
    }
} catch (IOException e) {
    System.err.println("Kopieren fehlgeschlagen: " + e.getMessage());
}

read() gibt entweder die Anzahl gelesener Bytes zurück oder -1, sobald das Dateiende erreicht ist — analog zu readLine() bei BufferedReader, das null statt -1 als Abbruchsignal nutzt. Bytes blockweise in einen Puffer zu lesen (statt einzeln mit read() ohne Argument) ist deutlich performanter, weil jeder Aufruf potenziell einen Systemaufruf auslöst — ein einzelnes Byte pro Aufruf würde bei einer großen Datei extrem viele solcher teuren Aufrufe verursachen. Für moderne Code-Basen bietet Files.copy(quelle, ziel) aus java.nio.file dieselbe Funktionalität deutlich kompakter, ohne die Puffer-Logik von Hand schreiben zu müssen.

Mehrere Ressourcen in einem try-with-resources

Wie im Kopierbeispiel oben gezeigt, lassen sich mehrere Ressourcen (hier: Quelle UND Ziel) in einem einzigen try (...)-Kopf öffnen, durch Semikolon getrennt. Wichtig dabei: Sie werden beim Verlassen des Blocks in UMGEKEHRTER Reihenfolge ihrer Deklaration geschlossen (hier also zuerst fos, dann fis) — relevant, wenn eine Ressource von einer anderen abhängt und in einer bestimmten Reihenfolge freigegeben werden muss.

Für kleine Dateien: Files.readAllBytes()

Wenn die gesamte Datei ohnehin komplett in den Speicher passt (z. B. eine Konfigurationsdatei im Kilobyte-Bereich), ist der manuelle Umgang mit FileInputStream und Puffer-Schleifen oft unnötiger Aufwand:

byte[] inhalt = Files.readAllBytes(Path.of("konfig.bin")); // liest alles auf einmal

Der Kompromiss: Bei großen Dateien (mehrere hundert MB oder GB) würde diese Methode den kompletten Inhalt gleichzeitig im Arbeitsspeicher halten — für solche Fälle bleibt das blockweise Lesen mit FileInputStream (oder ein NIO-Channel mit Memory-Mapping) die speicherschonendere Wahl.

Gepuffert vs. ungepuffert

Ein rohes FileInputStream liest direkt vom Betriebssystem, ohne eigenen Zwischenspeicher — wird read() oft mit kleinen Datenmengen aufgerufen, kann ein BufferedInputStream als Wrapper die Anzahl teurer Systemaufrufe deutlich reduzieren:

try (InputStream in = new BufferedInputStream(new FileInputStream("grosse-datei.bin"))) {
    int b;
    while ((b = in.read()) != -1) { // liest jetzt aus dem internen Puffer, nicht jedes Mal vom OS
        // verarbeite einzelnes Byte
    }
}

BufferedInputStream puffert dabei intern selbst schon in größeren Blöcken, auch wenn der aufrufende Code Byte für Byte liest — das Muster “immer puffern, wenn byteweise oder zeichenweise gelesen wird” gilt in Java durchgängig, egal ob für Dateien, Netzwerkverbindungen oder andere Streams.

Siehe auch: I/O Streams, FileOutputStream, Files, try-with-resources