2016-04-05 26 views
25

ASP.NET Core'da web api'ye büyük dosyalar yüklediğimde, çalışma zamanı, işlevimin işlenmesinden ve saklanmasının kaydedilmesinden önce dosyayı belleğe yükler. . Büyük yüklemelerle bu hem yavaş hem de daha fazla bellek gerektirdiği için bir sorun haline gelir. ASP.NET there are some articles'un önceki sürümlerinde, isteklerin arabelleğe alınmasını devre dışı bırakma hakkında ancak ASP.NET Çekirdeği ile nasıl yapılacağına dair herhangi bir bilgi bulamıyorum. İsteklerin arabelleğe alınmasını devre dışı bırakmak mümkün mü, bu yüzden sunucumda her zaman bellek yetersiz kalmıyorum?ASP.NET Core 1.0 üzerinde büyük dosya yüklemeleri ile başa çıkmak

+0

[flowjs] (https://github.com/flowjs/flow.js) – jltrem

+0

Merhaba @jltrem için api'ye göre küçük dosyalar halinde dosya yüklemeyi desteklemek için dosya yükleme arka ucumu yazarım, asp.net core'i paylaşabilir misiniz flowjs kullanarak yüklenen dosyaları idare eden kontrolör ve açısal kod? –

cevap

18

kullanımlar Microsoft.AspNetCore.WebUtilities.MultipartReader o ...

en az arabellek [ile] herhangi bir akış ayrıştırabilir için. Size her bölümün başlıklarını ve gövdesini birer birer verir ve sonra bu bölümün gövdesiyle istediğiniz şeyi yaparsınız (tampon, atma, diske yaz, vs.).

İşte bir orta sınıf örneği.

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

İşte yardımcılar.

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

Dış Kaynaklar

yılında
+1

Bu kodun dnx451 üzerinde mükemmel bir şekilde çalıştığı, ancak dnxcore50'de bir bellek sızıntısına sahip olduğu anlaşılıyor. RC2 için sabit olan bir şey olabilir. Yukarıdaki snippet'ten GetFileName() – Martin

+1

Sunucunun tüm dosyayı arabelleğe almasına neden olur. Onu rastgele bir dosya adıyla değiştirdim, ancak tüm parçaları bir dosyaya yazdıktan sonra, bu bellek kullanımının dosya boyutuna eşit olarak arttığını görüyorum. Eğer daha fazla GC bundan kurtulacaksa, tamam olabilir. Ancak – EvAlex

+0

@EvAlex 'GetFileName' gibi bir dize tamamlanmamış gibi görünüyor. Sunucunun tüm dosyayı arabelleğe almasına neden olabilen, öncesinde veya sonrasında gerçekleşen başka bir şey var mı? Kodu paylaşmak için –

2

senin Controller sadece dosyalara erişmek için Request.Form.Files kullanabilirsiniz:

[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(); 
} 
İlgili konular