Streaming di file di grandi dimensioni su ASP.NET MVC

Per un’applicazione su cui sto lavorando, devo consentire all’utente di caricare file molto grandi, ad esempio potenzialmente molti gigabyte, tramite il nostro sito web. Sfortunatamente, ASP.NET MVC sembra caricare l’intera richiesta nella RAM prima di iniziare a servirla, non esattamente l’ideale per tale applicazione. In particolare, cercando di aggirare il problema tramite codice come il seguente:

if (request.Method == "POST") { request.ContentLength = clientRequest.InputStream.Length; var rgbBody = new byte[32768]; using (var requestStream = request.GetRequestStream()) { int cbRead; while ((cbRead = clientRequest.InputStream.Read(rgbBody, 0, rgbBody.Length)) > 0) { fileStream.Write(rgbBody, 0, cbRead); } } } 

non riesce a eludere la mentalità buffer-the-request-in-RAM. C’è un modo semplice per aggirare questo comportamento?

Si scopre che il mio codice iniziale era fondamentalmente corretto; l’unica modifica richiesta era cambiare

 request.ContentLength = clientRequest.InputStream.Length; 

a

 request.ContentLength = clientRequest.ContentLength; 

I precedenti flussi nell’intera richiesta per determinare la lunghezza del contenuto; il secondo controlla semplicemente l’intestazione Content-Length , che richiede solo che le intestazioni siano state inviate per intero. Ciò consente a IIS di avviare lo streaming della richiesta quasi immediatamente, eliminando completamente il problema originale.

Certo, puoi farlo. Vedi Upload di file RESTful con HttpWebRequest e IHttpHandler . Ho usato questo metodo per alcuni anni e ho un sito che è stato testato con file di almeno diversi gigabyte. In sostanza, si desidera creare il proprio IH HTTPHandler, che è più facile di quanto sembri.

In poche parole, si crea una class che implementa l’interfaccia IHttpHandler, il che significa che è necessario supportare la proprietà IsReusable e il metodo ProcessRequest. Oltre a ciò, c’è una piccola modifica al tuo web.config e funziona come un incantesimo. A questo punto del ciclo di vita della richiesta, l’intero file che viene caricato non viene caricato in memoria, quindi evita accuratamente i problemi di memoria.

Si noti che in web.config,

    

il file referenziato, DocumentUploadService.upl, in realtà non esiste. Questo è solo lì per dare un’estensione alternativa in modo che la richiesta non venga intercettata dal gestore standard. Si punta il modulo di caricamento file su quel percorso, ma poi la class FileUploadHandler si triggers e riceve effettivamente il file.

Aggiornamento: In realtà, il codice che uso è diverso da quello dell’articolo, e penso di essermi imbattuto nel motivo per cui funziona. Utilizzo la class HttpPostedFile , in cui “I file vengono caricati in formato MEST multipart / form-data. Per impostazione predefinita, tutte le richieste, inclusi i campi modulo e i file caricati, maggiori di 256 KB vengono memorizzate su disco, anziché conservate nella memoria del server. “

 if (context.Request.Files.Count > 0) { string tempFile = context.Request.PhysicalApplicationPath; for(int i = 0; i < context.Request.Files.Count; i++) { HttpPostedFile uploadFile = context.Request.Files[i]; if (uploadFile.ContentLength > 0) { uploadFile.SaveAs(string.Format("{0}{1}{2}", tempFile,"Upload\\", uploadFile.FileName)); } } }