Лучший способ потоковой передачи файлов в ASP.NET

Какой лучший способ потоковой передачи файлов с помощью ASP.NET?

Для этого существуют различные методы, и я в настоящее время использую метод Response.TransmitFile() внутри обработчика http, который отправляет файл в браузер напрямую. Это используется для различных вещей, включая отправку FLV извне webroot во встроенный Flash-плеер.

Однако это не похоже на надежный метод. В частности, есть странная проблема с Internet Explorer (7), где браузер просто зависает после просмотра видео или двух. Нажатие на какие-либо ссылки и т.д. Не имеет никакого эффекта, и единственный способ заставить вещи снова работать на сайте - закрыть браузер и снова открыть его.

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

Попробовав несколько разных вещей, я обнаружил, что для меня работает следующий метод:

Response.WriteFile(path);
Response.Flush();
Response.Close();
Response.End();

Это касается проблемы, упомянутой выше, и просмотр видео больше не приводит к зависанию Internet Explorer.

Тем не менее, я понимаю, что Response.WriteFile() сначала загружает файл в память, и при условии, что некоторые потоковые файлы могут потенциально быть довольно большим, это не похоже на идеальное решение.

Мне интересно узнать, как другие разработчики пересылают большие файлы в ASP.NET и, в частности, потоковые видеофайлы FLV.

Ответ 1

Я бы взял вещи за пределами конвейера aspx. В частности, я бы написал обработчик (ashx, или сопоставленный через config), который выполняет минимальную работу и просто записывает ответ в куски. Обработчик будет принимать входные данные из строки запроса/формы как обычно, находить объект в потоке и передавать данные (используя локальный буфер среднего размера в цикле). Простой (неполный) пример, показанный ниже:

public void ProcessRequest(HttpContext context) {
    // read input etx
    context.Response.Buffer = false;
    context.Response.ContentType = "text/plain";
    string path = @"c:\somefile.txt";
    FileInfo file = new FileInfo(path);
    int len = (int)file.Length, bytes;
    context.Response.AppendHeader("content-length", len.ToString());
    byte[] buffer = new byte[1024];
    Stream outStream = context.Response.OutputStream;
    using(Stream stream = File.OpenRead(path)) {
        while (len > 0 && (bytes =
            stream.Read(buffer, 0, buffer.Length)) > 0)
        {
            outStream.Write(buffer, 0, bytes);
            len -= bytes;
        }
    }
}

Ответ 2

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

HTTP-протокол поддерживает запросы байтов с распределенным байтом и возобновляемые загрузки, и многие потоковые клиенты (например, видеоплееры или Adobe pdf) могут и будут пытаться объединить их, сэкономить пропускную способность и дать вашим пользователям лучший опыт.

Не тривиально, но это время хорошо проведено.

Ответ 3

Попробуйте открыть файл в виде потока, а затем с помощью Response.OutputStream.Write(). Например:

Изменить: Плохо, я забыл, что Write берет байтовый буфер. Фиксированный

byte [] buffer = new byte[1<<16] // 64kb
int bytesRead = 0;
using(var file = File.OpenRead(path))
{
   while((bytesRead = file.Read(buffer, 0, buffer.Length)) != 0)
   {
        Response.OutputStream.Write(buffer, 0, bytesRead);
   }
}
Response.Flush();
Response.Close();
Response.End();

Изменить 2: Пробовали ли вы это? Он должен работать.

Ответ 4

После попытки множества разных комбинаций, в том числе кода, размещенного в различных ответах, кажется, что установка Response.Buffer = true перед вызовом TransmitFile сделала трюк, и веб-приложение стало намного более восприимчивым в Internet Explorer.

В этом конкретном случае расширение SWF также отображается на ASP.NET, и мы используем собственный обработчик в нашем веб-приложении для чтения файлов с диска, а затем отправляем их в браузер с помощью Response.TransmitFile(), У нас есть Flash-видеопроигрыватель для воспроизведения видеофайлов, которые также являются SWF, и я думаю, что все эти действия проходят через обработчик без буферизации, что, возможно, вызывало странные вещи в IE.