it-swarm.com.de

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 kann mehrere Dateien gleichzeitig lesen (verschiedene Seitenanforderungen), daher suche ich nach der optimalen Methode, um dies zu erreichen, ohne die CPU zu stark zu belasten. Ist der Code unten 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;
}
364
Tony_Henrich

Ersetzen Sie einfach das Ganze durch:

return File.ReadAllBytes(fileName);

Wenn Sie sich jedoch Gedanken über den Speicherverbrauch machen, sollten Sie nicht die gesamte Datei auf einmal in den Speicher einlesen. Sie sollten das in Stücken tun.

739
Mehrdad Afshari

Ich könnte argumentieren, dass die Antwort hier im Allgemeinen "nicht" ist. Sofern Sie nicht unbedingt alle Daten auf einmal benötigen , sollten Sie die Verwendung einer Stream -basierten API (oder einer Variante eines Readers/Iterators) in Betracht ziehen. Dies ist besonders wichtig , wenn Sie mehrere parallele Operationen ausführen (wie in der Frage vorgeschlagen), um die Systemlast zu minimieren und den Durchsatz zu maximieren.

Wenn Sie beispielsweise 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);
    }
}
64
Marc Gravell

Ich würde das denken:

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

Ihr Code kann hierauf angerechnet 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 Integer.MaxValue - Dateigrößenbeschränkung durch die Read-Methode. Mit anderen Worten, Sie können nur einen Teil von 2 GB gleichzeitig lesen.

Beachten Sie auch, dass das letzte Argument für den FileStream eine Puffergröße ist.

Ich würde auch vorschlagen, über FileStream und BufferedStream zu lesen.

Wie immer ist ein einfaches Beispielprogramm zum Profilieren, das am schnellsten ist, am vorteilhaftesten.

Auch Ihre zugrunde liegende Hardware hat einen großen Einfluss auf die Leistung. Verwenden Sie serverbasierte Festplatten mit großen Caches und einer RAID-Karte mit integriertem Speichercache? Oder verwenden Sie ein Standardlaufwerk, das an den Port IDE angeschlossen ist?

25
user113476

Abhängig von der Häufigkeit der Vorgänge, der Größe der Dateien und der Anzahl der Dateien, die Sie betrachten, sind weitere Leistungsprobleme zu berücksichtigen. Beachten Sie, dass jedes Ihrer Bytearrays dem Garbage Collector ausgeliefert wird. Wenn Sie keine dieser Daten zwischenspeichern, kann dies dazu führen, dass Sie viel Müll erzeugen und den größten Teil Ihrer Leistung an % Zeit in GC verlieren. Wenn die Chunks größer als 85 KB sind, werden Sie dem Large Object Heap (LOH) zuweisen, für dessen Freigabe eine Sammlung aller Generationen erforderlich ist (dies ist sehr teuer und wird auf einem Server die gesamte Ausführung während des Vorgangs anhalten) ). Darüber hinaus kann es bei einer Unmenge von Objekten auf dem LOH zu einer LOH-Fragmentierung kommen (der LOH wird nie komprimiert), was zu einer schlechten Leistung und Ausnahmen aufgrund unzureichenden Speichers führt. Sie können den Vorgang wiederholen, sobald Sie einen bestimmten Punkt erreicht haben, aber ich weiß nicht, ob dies eine bewährte Methode ist.

Der Punkt ist, dass Sie den gesamten Lebenszyklus Ihrer App berücksichtigen sollten, bevor Sie alle Bytes auf schnellstem Weg in den Speicher einlesen. Andernfalls tauschen Sie möglicherweise kurzfristige Leistung gegen Gesamtleistung.

9
Joel

Ich würde sagen, BinaryReader ist in Ordnung, kann aber dahingehend überarbeitet werden, anstatt all dieser Codezeilen zum Abrufen der Länge des Puffers:

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 in der oberen Antwort, die .ReadAllBytes() enthält, gesehen habe, dass einer der Kommentatoren Probleme mit Dateien> 600 MB hatte, da ein BinaryReader gemeint ist für so etwas. Das Einfügen in eine using -Anweisung stellt außerdem sicher, dass FileStream und BinaryReader geschlossen und entsorgt werden.

6
vapcguy

Falls mit 'eine große Datei' mehr als 4 GB gemeint sind, ist meine folgende geschriebene Codelogik angemessen. Das Hauptproblem ist der LONG-Datentyp, der mit der SEEK-Methode verwendet wird. Da ein LONG in der Lage ist, über 2 ^ 32 Datengrenzen hinaus zu zeigen. In diesem Beispiel verarbeitet der Code zuerst die große Datei in Blöcken von 1 GB. Nachdem die großen ganzen 1 GB-Blöcke verarbeitet wurden, werden die verbleibenden (<1 GB) Bytes verarbeitet. Ich verwende diesen Code bei der Berechnung des CRC von Dateien, die die Größe von 4 GB überschreiten. (Verwenden von https://crc32c.machinezoo.com/ für die crc32c-Berechnung in diesem Beispiel)

private uint Crc32CAlgorithmBigCrc(string fileName)
{
    uint hash = 0;
    byte[] buffer = null;
    FileInfo fileInfo = new FileInfo(fileName);
    long fileLength = fileInfo.Length;
    int blockSize = 1024000000;
    decimal div = fileLength / blockSize;
    int blocks = (int)Math.Floor(div);
    int restBytes = (int)(fileLength - (blocks * blockSize));
    long offsetFile = 0;
    uint interHash = 0;
    Crc32CAlgorithm Crc32CAlgorithm = new Crc32CAlgorithm();
    bool firstBlock = true;
    using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
    {
        buffer = new byte[blockSize];
        using (BinaryReader br = new BinaryReader(fs))
        {
            while (blocks > 0)
            {
                blocks -= 1;
                fs.Seek(offsetFile, SeekOrigin.Begin);
                buffer = br.ReadBytes(blockSize);
                if (firstBlock)
                {
                    firstBlock = false;
                    interHash = Crc32CAlgorithm.Compute(buffer);
                    hash = interHash;
                }
                else
                {
                    hash = Crc32CAlgorithm.Append(interHash, buffer);
                }
                offsetFile += blockSize;
            }
            if (restBytes > 0)
            {
                Array.Resize(ref buffer, restBytes);
                fs.Seek(offsetFile, SeekOrigin.Begin);
                buffer = br.ReadBytes(restBytes);
                hash = Crc32CAlgorithm.Append(interHash, buffer);
            }
            buffer = null;
        }
    }
    //MessageBox.Show(hash.ToString());
    //MessageBox.Show(hash.ToString("X"));
    return hash;
}
1
Menno de Ruiter

benutze das:

 bytesRead = responseStream.ReadAsync(buffer, 0, Length).Result;
0
Hardik Raval

Verwenden Sie die BufferedStream-Klasse in C #, um die Leistung zu verbessern. Ein Puffer ist ein Byteblock im Speicher, der zum Zwischenspeichern von Daten verwendet wird, wodurch die Anzahl der Aufrufe des Betriebssystems verringert wird. Puffer verbessern die Lese- und Schreibleistung.

Im Folgenden finden Sie ein Codebeispiel und eine zusätzliche Erklärung: http://msdn.Microsoft.com/en-us/library/system.io.bufferedstream.aspx

0
Todd Moses