2016-04-05 33 views
25

Quando sto caricando file di grandi dimensioni sulla mia API Web in ASP.NET Core, il runtime caricherà il file in memoria prima che venga attivata la funzione per l'elaborazione e la memorizzazione del caricamento. . Con grandi caricamenti questo diventa un problema in quanto è lento e richiede più memoria. Per le versioni precedenti di ASP.NET there are some articles su come disabilitare il buffering delle richieste, ma non sono in grado di trovare alcuna informazione su come eseguire questa operazione con ASP.NET Core. È possibile disabilitare il buffering delle richieste in modo da non esaurire la memoria sul mio server tutto il tempo?Gestione di caricamenti di file di grandi dimensioni su ASP.NET Core 1.0

+0

Scrivo il mio back-end per il caricamento di file per supportare il caricamento di file in piccoli blocchi in base all'api per [flowjs] (https://github.com/flowjs/flow.js) – jltrem

+0

Ciao @jltrem, potresti condividere il nucleo asp.net controller e codice angolare che gestisce i file caricati tramite flowjs? –

risposta

18

Utilizzare il Microsoft.AspNetCore.WebUtilities.MultipartReader perché ...

in grado di analizzare qualsiasi flusso [con] il minimo buffering. Ti dà le intestazioni e il corpo di ogni sezione una alla volta e poi fai quello che vuoi con il corpo di quella sezione (buffer, scarta, scrivi su disco, ecc.).

Ecco un esempio di middleware.

app.Use(async (context, next) => 
{ 
    if (!IsMultipartContentType(context.Request.ContentType)) 
    { 
     await next(); 
     return; 
    } 

    var boundary = GetBoundary(context.Request.ContentType); 
    var reader = new MultipartReader(boundary, context.Request.Body); 
    var section = await reader.ReadNextSectionAsync(); 

    while (section != null) 
    { 
     // process each image 
     const int chunkSize = 1024; 
     var buffer = new byte[chunkSize]; 
     var bytesRead = 0; 
     var fileName = GetFileName(section.ContentDisposition); 

     using (var stream = new FileStream(fileName, FileMode.Append)) 
     { 
      do 
      { 
       bytesRead = await section.Body.ReadAsync(buffer, 0, buffer.Length); 
       stream.Write(buffer, 0, bytesRead); 

      } while (bytesRead > 0); 
     } 

     section = await reader.ReadNextSectionAsync(); 
    } 

    context.Response.WriteAsync("Done."); 
}); 

Ecco gli helper.

private static bool IsMultipartContentType(string contentType) 
{ 
    return 
     !string.IsNullOrEmpty(contentType) && 
     contentType.IndexOf("multipart/", StringComparison.OrdinalIgnoreCase) >= 0; 
} 

private static string GetBoundary(string contentType) 
{ 
    var elements = contentType.Split(' '); 
    var element = elements.Where(entry => entry.StartsWith("boundary=")).First(); 
    var boundary = element.Substring("boundary=".Length); 
    // Remove quotes 
    if (boundary.Length >= 2 && boundary[0] == '"' && 
     boundary[boundary.Length - 1] == '"') 
    { 
     boundary = boundary.Substring(1, boundary.Length - 2); 
    } 
    return boundary; 
} 

private string GetFileName(string contentDisposition) 
{ 
    return contentDisposition 
     .Split(';') 
     .SingleOrDefault(part => part.Contains("filename")) 
     .Split('=') 
     .Last() 
     .Trim('"'); 
} 

riferimenti esterni

+1

Sembra che questo codice funzioni perfettamente su dnx451 ma ha una perdita di memoria su dnxcore50. Potrebbe essere qualcosa che viene risolto per RC2 però. – Martin

+1

GetFileName() dallo snippet precedente provoca il buffer dell'intero file sul server. L'ho sostituito con un nome di file casuale, ma dopo aver scritto tutti i blocchi in un file, vedo che l'utilizzo della memoria è aumentato della quantità pari alla dimensione del file. Potrebbe essere in effetti ok, se un altro GC lo eliminerà. Ma sembra non essere ok – EvAlex

+0

@EvAlex 'GetFileName' solo analizza una stringa. C'è qualcos'altro che sta accadendo prima o dopo che potrebbe causare il server per bufferizzare l'intero file? –

2

Nel vostro Controller si può semplicemente utilizzare Request.Form.Files per accedere ai file:

[HttpPost("upload")] 
public async Task<IActionResult> UploadAsync(CancellationToken cancellationToken) 
{ 
    if (!Request.HasFormContentType) 
     return BadRequest(); 

    var form = Request.Form; 
    foreach(var formFile in form.Files) 
    { 
     using(var readStream = formFile.OpenReadStream()) 
     { 
      // Do something with the uploaded file 
     } 
    } 


    return Ok(); 
} 
Problemi correlati