2012-10-03 19 views
5

Mam aplikację do przeglądania plików w MVC4, która umożliwia pobranie wybranego pliku z kontrolera.ASP.NET MVC: zwracanie dużych ilości danych z FileResult

Obecnie FileResult zwraca strumień pliku wraz z innymi nagłówkami odpowiedzi. Chociaż działa to dobrze dla mniejszych plików, pliki, które są większe, generują wyjątek OutOfMemoryException.

Co chciałbym zrobić, to przesłać plik z kontrolera, bez buforowania w pamięci w sposób podobny do HttpReponse.TransmitFile w WebForms.

Jak można tego dokonać?

Odpowiedz

2

Tak można za pomocą Web API aby strumień pliku, spojrzeć na ten artykuł Dealing with large files in ASP.NET Web API

+0

Dzięki - w przypadku korzystania z łącza, po dodaniu 'GlobalConfiguration.Configuration.Services.Replace (typeof (WebHostBufferPolicySelector), nowe NoBufferPolicySelector());' linię do mojego pliku Global.asax.cs, otrzymuję następujący błąd: 'Typ usługi WebHostBufferPolicySelector nie jest obsługiwany. Czy jest tam jakaś pomoc? –

+0

Pytanie dotyczy MVC, a nie Web API –

0

Dodatkowo do powyższego linku opisującej konfigurację, można użyć TransmitFile plików „strumień”. Jednak TransmitFile ma trochę wstecz na niektórych klientach HTTP.

To jest mój kod do "strumieniowania" plików do klienta, jako alternatywa dla TransmitFile. Zauważ, że mam mieć config stałą wycofywania z WriteFile (które mają ten sam problem jak ty OutOfMemory): kod

private void RenderContent(string contentType,string fileName, bool inline); 

metoda:

FileInfo fi = new FileInfo(fileName); 

Response.ClearHeaders(); 
Response.ClearContent(); 

Response.ContentType = contentType; 
Response.CacheControl = "No-cache"; 
Response.Cache.SetCacheability(System.Web.HttpCacheability.NoCache); 
Response.AddHeader("Content-Length", fi.Length.ToString()); 
Response.AddHeader("Content-Disposition", (inline ? "inline" : "attachment") + "; filename=\"" + fi.Name + "\""); 

if (cAppInfos.Constant["FallBackToWriteFile"] != null && cAppInfos.Constant["FallBackToWriteFile"] == "true") 
{ 
    Response.WriteFile(fileName); 
} 
else 
{ 
    int chunkSize = 8192; 
    byte[] buffer = new byte[chunkSize]; 
    int offset = 0; 
    int read = 0; 
    using (FileStream fs = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)) 
    { 
     while ((read = fs.Read(buffer, offset, chunkSize)) > 0) 
     { 
      if (!Response.IsClientConnected) 
       break; 

      Response.OutputStream.Write(buffer, 0, read); 
      Response.Flush(); 
     } 
    } 
} 

Użyłem tego kodu, aby uniknąć pewnych problemów przesyłanie plików PDF do starej wtyczki programu Acrobat Reader. Dla parametru inline możesz użyć "false".

Dodatkowo, możesz użyć polecenia try/catch wokół tego kodu, ponieważ dowolny z Response.IsClientConnecter/Write/Flush może zgłaszać wyjątki, jeśli klient się rozłączy.

Jednak nie używam MVC i nie jestem pewien, czy jest to akceptowalne dla tego techno, jak gdybyś nie użył TransmitFile, możesz utknąć z tym samym problemem z tym kodem. Jeśli próbujesz osadzić pliki w elemencie stron internetowych (jak może być w przypadku base64?), To nie jest to rozwiązanie.

Daj mi znać więcej informacji na temat Twoich wymagań, jeśli potrzebujesz innych sposobów na osiągnięcie swoich celów.

+0

Dodatkowo, wybór przedstawienia wyniku jako "plików strumieniowych" może nie mieć znaczenia w kontrolerze i może zostać przeniesiony w prezentacji, w której uzyskasz dostęp do obiektu Response. Kontroler może po prostu poprosić warstwę prezentacji o strumień ... filestream/filename? – Echtelion

+0

W odpowiedzi na "kontroler może po prostu poprosić warstwę prezentacji o strumień ..." komentarz, ponieważ musiałby obsłużyć strumień w ten sam sposób, czy po prostu wprowadziłby ten sam problem w innej lokalizacji? –

+0

Nie jestem ekspertem MVC, ale proponuję przenieść problem w miejscu, w którym możesz go rozwiązać (np. Miejsce z dostępem do obiektu Response). – Echtelion

8

Możesz wyłączyć bufor odpowiedzi, zanim zwrócisz wynik pliku.

Response.BufferOutput = false; 
return File(fileStream, contentType); 
+1

To jedyne (czyste) rozwiązanie, które działało dla mnie , dzięki! –

+1

Należy zauważyć, że w przypadku naprawdę dużych plików (2 GB +) należy również ustawić 'HttpCompletionOption.ResponseHeadersRead', aby zapobiec' Nie można zapisać więcej bajtów do bufora niż skonfigurowany maksymalny rozmiar bufora: 2147483647. ". –

Powiązane problemy