2016-04-05 14 views
25

Kiedy przesyłam duże pliki do mojego interfejsu API w ASP.NET Core, środowisko wykonawcze załaduje plik do pamięci, zanim moja funkcja do przetwarzania i przechowywania wysyłania zostanie zwolniona . W przypadku dużych przesyłek staje się to problemem, ponieważ jest powolny i wymaga więcej pamięci. Wcześniejszych wersjach ASP.NET there are some articles, jak wyłączyć buforowanie żądań, ale nie jestem w stanie znaleźć żadnych informacji, jak to zrobić z ASP.NET Core. Czy można wyłączyć buforowanie żądań, aby nie zabrakło pamięci na moim serwerze przez cały czas?Radzenie sobie z przesyłaniem dużych plików na ASP.NET Core 1.0

+0

Piszę swój backend do przesyłania plików, aby obsługiwał przesyłanie plików w małych porcjach zgodnie z api dla [flowjs] (https://github.com/flowjs/flow.js) – jltrem

+0

Witaj @jltrem, czy mógłbyś podzielić się rdzeniem asp.net? kontroler i kod kątowy, który obsługuje pliki przesłane za pomocą flowjs? –

Odpowiedz

18

pomocą Microsoft.AspNetCore.WebUtilities.MultipartReader ponieważ ...

można analizować każdą strumień [z] minimalnym buforowania. Daje ci nagłówki i treść każdej sekcji po jednej na raz, a następnie robisz, co chcesz, z treścią tej sekcji (bufor, odrzuć, napisz na dysk itp.).

Oto przykład oprogramowania pośredniego.

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

Oto pomocnicy.

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('"'); 
} 

Odniesienia zewnętrzne

+1

Wygląda na to, że ten kod działa idealnie na dnx451, ale ma wyciek pamięci na dnxcore50. Może to być coś, co jest naprawiane dla RC2. – Martin

+1

Funkcja GetFileName() z powyższego fragmentu powoduje, że serwer buforuje cały plik. Zastąpiłem go losową nazwą pliku, ale po zapisaniu wszystkich porcji do pliku widzę, że użycie pamięci zwiększyło się o amefount równą rozmiarowi pliku. Może być w porządku, jeśli dalsze GC się go pozbędzie. Ale wydaje się, że nie jest dobrze – EvAlex

+0

@EvAlex 'GetFileName' po prostu analizuje ciąg znaków. Czy istnieje coś innego, co dzieje się przed lub po tym, może spowodować, że serwer buforuje cały plik? –

2

w twojej Controller można po prostu użyć Request.Form.Files aby uzyskać dostęp do plików:

[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(); 
} 
Powiązane problemy