Лучший способ прочитать большой файл в массив байтов в C#?



У меня есть веб-сервер, который будет читать бинарные файлы большого размера (несколько мегабайт) в байтовые массивы. Сервер может одновременно читать несколько файлов (разные запросы страниц), поэтому я ищу наиболее оптимизированный способ сделать это, не слишком обременяя процессор. Является ли приведенный ниже код достаточно хорошим?



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;
}
1698   9  

9 ответов:

просто замените все это на:

return File.ReadAllBytes(fileName);

однако, если вы обеспокоены потреблением памяти, вы должны не читать весь файл в память сразу все на всех. Вы должны делать это кусками.

Я могу утверждать, что ответ тут вообще "не". Если только ты совершенно все данные сразу, рассмотрите возможность использования Stream-основанный API (или некоторый вариант читателя / итератора). То есть особенно важно, когда у вас есть несколько параллельных операций (как предложено в вопросе), чтобы минимизировать нагрузку на систему и максимизировать пропускную способность.

например, если вы передаете данные вызывающему абоненту:

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);
    }
}

Я думаю так:

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

ваш код может быть отнесен к этому (вместо файла.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;
} 

обратите внимание на целое число.MaxValue-ограничение размера файла, устанавливаемое методом Read. Другими словами, вы можете прочитать только кусок 2GB сразу.

также обратите внимание, что последним аргументом для FileStream является размер буфера.

Я бы также предложил прочитать о FileStream и BufferedStream.

Как всегда простой пример программы для профилирования что быстрее всего будет наиболее выгодно.

также ваше базовое оборудование будет иметь большое влияние на производительность. Вы используете серверные жесткие диски с большими кэшами и RAID-карту с встроенным кэшем памяти? Или вы используете стандартный диск, подключенный к порту IDE?

в зависимости от частоты операций, размера файлов и количества файлов, которые вы просматриваете, есть и другие проблемы производительности, которые следует учитывать. Одна вещь, которую нужно помнить, заключается в том, что каждый из ваших байтовых массивов будет выпущен на милость сборщика мусора. Если вы не кэшируете какие-либо из этих данных, вы можете в конечном итоге создать много мусора и потерять большую часть своей производительности в % времени в GC. Если куски больше, чем 85K, вы будете выделение кучи больших объектов (LOH), для освобождения которой потребуется коллекция всех поколений (это очень дорого, и на сервере будет останавливаться все выполнение во время его выполнения). Кроме того, если у вас есть тонна объектов на LOH, вы можете получить фрагментацию LOH (LOH никогда не уплотняется), что приводит к низкой производительности и исключениям из памяти. Вы можете переработать процесс, как только вы достигнете определенной точки, но я не знаю, является ли это лучшей практикой.

в дело в том, что вы должны рассмотреть полный жизненный цикл вашего приложения, прежде чем обязательно просто считывать все байты в память самым быстрым способом, или вы можете торговать краткосрочной производительностью для общей производительности.

Я бы сказал BinaryReader это нормально, но может быть рефакторинг на это, вместо всех этих строк кода для получения длины буфера:

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;
}

должно быть лучше, чем при использовании .ReadAllBytes(), так как я видел в комментариях на первом месте, что включает в себя .ReadAllBytes() что у одного из комментаторов были проблемы с файлами > 600 MB, так как A BinaryReader предназначен для такого рода вещи. Кроме того, положить его в using заявление гарантирует FileStream и BinaryReader закрываются и утилизируются.

используйте класс BufferedStream в C# для повышения производительности. Буфер представляет собой блок байтов в памяти, используемый для кэширования данных, тем самым уменьшая количество вызовов операционной системы. Буферы повышают производительность чтения и записи.

см. ниже пример кода и дополнительные пояснения: http://msdn.microsoft.com/en-us/library/system.io.bufferedstream.aspx

Я бы рекомендовал пробовать Response.TransferFile() метод тогда a Response.Flush() и Response.End() для обслуживания больших файлов.

Если вы имеете дело с файлами выше 2 ГБ, вы обнаружите, что вышеуказанные методы не работают.

гораздо проще просто передать поток в MD5 и позвольте этому фрагментировать ваш файл для вас:

private byte[] computeFileHash(string filename)
{
    MD5 md5 = MD5.Create();
    using (FileStream fs = new FileStream(filename, FileMode.Open))
    {
        byte[] hash = md5.ComputeHash(fs);
        return hash;
    }
}

Comments

    Ничего не найдено.