496 Stimmen

Beste Weg, um eine große Datei in ein Byte-Array in C# zu lesen?

Ich habe einen Webserver, der große Binärdateien (mehrere Megabyte) in Byte-Arrays liest. Der Server könnte mehrere Dateien gleichzeitig lesen (verschiedene Seitenanfragen), so dass ich auf der Suche nach dem optimierten Weg, dies zu tun, ohne die CPU zu sehr zu belasten. Ist der folgende Code gut genug?

public byte[] FileToByteArray(string fileName)
{
    byte[] buff = null;
    FileStream fs = new FileStream(fileName, 
                                   FileMode.Open, 
                                   FileAccess.Read);
    BinaryReader br = new BinaryReader(fs);
    long numBytes = new FileInfo(fileName).Length;
    buff = br.ReadBytes((int) numBytes);
    return buff;
}

78 Stimmen

Ihr Beispiel kann wie folgt abgekürzt werden byte[] buff = File.ReadAllBytes(fileName) .

3 Stimmen

Warum bedeutet die Tatsache, dass es sich um einen Webservice eines Drittanbieters handelt, dass die Datei vollständig im RAM sein muss, bevor sie an den Webservice gesendet wird, und nicht gestreamt? Der Webservice wird den Unterschied nicht erkennen.

0 Stimmen

@Brian, Einige Clients wissen nicht, wie sie mit einem .NET-Stream umgehen sollen, wie zum Beispiel Java. Wenn dies der Fall ist, kann nur die gesamte Datei in einem Byte-Array gelesen werden.

931voto

mmx Punkte 400975

Ersetzen Sie das Ganze einfach durch:

return File.ReadAllBytes(fileName);

Wenn Sie jedoch über den Speicherverbrauch besorgt sind, sollten Sie no die gesamte Datei auf einmal in den Speicher zu lesen. Sie sollten das in Abschnitten tun.

60 Stimmen

Diese Methode ist auf 2^32-Byte-Dateien (4,2 GB) beschränkt

20 Stimmen

File.ReadAllBytes löst bei großen Dateien eine OutOfMemoryException aus (getestet mit einer 630 MB großen Datei und es schlug fehl)

6 Stimmen

@juanjo.arana Ja, nun... natürlich wird es immer etwas geben, das nicht in den Speicher passt, und in diesem Fall gibt es keine Antwort auf die Frage. Generell sollte man die Datei streamen und nicht im Speicher ablegen. Sie könnten dies als Notlösung in Betracht ziehen: msdn.microsoft.com/de-us/library/hh285054%28v=vs.110%29.aspx

87voto

Marc Gravell Punkte 970173

Ich würde sagen, dass die Antwort hier allgemein ist "nicht". Es sei denn, Sie unbedingt notwendig alle Daten auf einmal zu erfassen, sollten Sie ein Stream -basierten API (oder einer Variante von Reader/Iterator). Das heißt insbesondere wichtig, wenn Sie mehrere parallele Operationen durchführen (wie in der Frage vorgeschlagen), um die Systemlast zu minimieren und den Durchsatz zu maximieren.

Zum Beispiel, wenn Sie Daten an einen Anrufer streamen:

Stream dest = ...
using(Stream source = File.OpenRead(path)) {
    byte[] buffer = new byte[2048];
    int bytesRead;
    while((bytesRead = source.Read(buffer, 0, buffer.Length)) > 0) {
        dest.Write(buffer, 0, bytesRead);
    }
}

4 Stimmen

Um Ihre Aussage zu ergänzen, schlage ich sogar vor, async ASP.NET-Handler in Betracht zu ziehen, wenn Sie eine E/A-gebundene Operation wie das Streaming einer Datei an den Client haben. Allerdings, wenn Sie müssen die gesamte Datei in eine byte[] Aus irgendeinem Grund schlage ich vor, keine Streams oder etwas anderes zu verwenden und nur die vom System bereitgestellte API zu nutzen.

0 Stimmen

@Mehrdad - Ich stimme zu; aber der gesamte Kontext ist nicht klar. Ebenso hat MVC Aktion-Ergebnisse für diese.

0 Stimmen

Ja, ich brauche alle Daten auf einmal. Sie gehen an einen Webservice eines Drittanbieters.

46voto

Powerlord Punkte 84404

Ich würde dies denken:

byte[] file = System.IO.File.ReadAllBytes(fileName);

8 Stimmen

Beachten Sie, dass dies bei sehr großen Dateien zum Stillstand führen kann.

38voto

Ihr Code kann hierauf abgestimmt werden (anstelle von File.ReadAllBytes):

public byte[] ReadAllBytes(string fileName)
{
    byte[] buffer = null;
    using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
    {
        buffer = new byte[fs.Length];
        fs.Read(buffer, 0, (int)fs.Length);
    }
    return buffer;
} 

Beachten Sie die Begrenzung der Dateigröße durch die Read-Methode (Integer.MaxValue). Mit anderen Worten: Sie können nur einen 2 GB großen Brocken auf einmal lesen.

Beachten Sie auch, dass das letzte Argument des FileStream eine Puffergröße ist.

Ich würde auch empfehlen, etwas über FileStream y BufferedStream .

Wie immer ist ein einfaches Beispielprogramm zur Erstellung eines Profils, das am schnellsten ist, von großem Nutzen.

Auch die zugrunde liegende Hardware hat einen großen Einfluss auf die Leistung. Verwenden Sie serverbasierte Festplattenlaufwerke mit großem Cache und eine RAID-Karte mit integriertem Speicher-Cache? Oder verwenden Sie ein Standardlaufwerk, das an den IDE-Anschluss angeschlossen ist?

0 Stimmen

Warum sollte die Art der Hardware einen Unterschied machen? Wenn es IDE ist, verwenden Sie also eine .NET-Methode und wenn es RAID ist, verwenden Sie eine andere?

0 Stimmen

@Tony_Henrich - Es hat nichts damit zu tun, welche Aufrufe Sie in Ihrer Programmiersprache machen. Es gibt verschiedene Arten von Festplattenlaufwerken. Beispielsweise werden die Festplatten von Seagate als "AS" oder "NS" klassifiziert, wobei "NS" die Server-Festplatte mit großem Cache ist, während "AS" die Festplatte für den Heimcomputer ist. Suchgeschwindigkeiten und interne Übertragungsraten beeinflussen ebenfalls, wie schnell Sie etwas von der Festplatte lesen können. RAID-Arrays können die Lese-/Schreibleistung durch Caching erheblich verbessern. Sie können also die Datei vielleicht auf einmal lesen, aber die zugrunde liegende Hardware ist immer noch der entscheidende Faktor.

2 Stimmen

Dieser Code enthält einen kritischen Fehler. Read muss nur mindestens 1 Byte zurückgeben.

13voto

vapcguy Punkte 6445

Ich würde sagen BinaryReader ist in Ordnung, kann aber auf diese Weise umgestaltet werden, anstatt all dieser Codezeilen, um die Länge des Puffers zu ermitteln:

public byte[] FileToByteArray(string fileName)
{
    byte[] fileData = null;

    using (FileStream fs = File.OpenRead(fileName)) 
    { 
        using (BinaryReader binaryReader = new BinaryReader(fs))
        {
            fileData = binaryReader.ReadBytes((int)fs.Length); 
        }
    }
    return fileData;
}

Sollte besser sein als die Verwendung von .ReadAllBytes() da ich in den Kommentaren zu der oberen Antwort gesehen habe, dass auch .ReadAllBytes() dass einer der Kommentatoren Probleme mit Dateien > 600 MB hatte, da ein BinaryReader ist für diese Art von Dingen gedacht. Auch das Einlegen in eine using Anweisung stellt sicher, dass die FileStream y BinaryReader werden geschlossen und entsorgt.

0 Stimmen

Für C# müssen Sie "using (FileStream fs = File.OpenRead(fileName))" anstelle von "using (FileStream fs = new File.OpenRead(fileName))" wie oben angegeben verwenden. Entfernen Sie einfach das Schlüsselwort new vor File.OpenRead()

0 Stimmen

@Syed Der obige Code WURDE für C# geschrieben, aber Sie haben Recht, dass new wurde dort nicht gebraucht. Entfernt.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X