ASP.NET CoreのWeb APIに大きなファイルをアップロードする場合、アップロードの処理と保存のための機能が実行される前に、ランタイムがファイルをメモリにロードします。大規模なアップロードでは、低速であり、より多くのメモリが必要になるため、これが問題になります。 ASP.NETの以前のバージョンの場合 いくつかの記事があります リクエストのバッファリングを無効にする方法についてですが、ASP.NET Coreでこれを行う方法に関する情報を見つけることができません。リクエストのバッファリングを無効にして、サーバーのメモリが常に不足しないようにすることは可能ですか?
使用 Microsoft.AspNetCore.WebUtilities.MultipartReader
それは...
最小限のバッファリングで[すべての]ストリームを解析できます。各セクションのヘッダーと本文を一度に1つずつ提供し、そのセクションの本文で必要なことを行います(バッファー、破棄、ディスクへの書き込みなど)。
ミドルウェアの例を次に示します。
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.");
});
ヘルパーは次のとおりです。
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('"');
}
外部参照
Shaun Luttinの答えは素晴らしく、今では彼が示した多くの仕事がASP.Net Core 2.2によって提供されています。
境界を取得します。
// Microsoft.AspNetCore.Http.Extensions.HttpRequestMultipartExtensions
var boundary = Request.GetMultipartBoundary();
if (string.IsNullOrWhiteSpace(boundary))
return BadRequest();
それでも次のようなセクションが表示されます。
var reader = new MultipartReader(boundary, Request.Body);
var section = await reader.ReadNextSectionAsync();
性質を確認し、FileMultipartSectionに変換します。
if (section.GetContentDispositionHeader())
{
var fileSection = section.AsFileSection();
var fileName = fileSection.FileName;
using (var stream = new FileStream(fileName, FileMode.Append))
await fileSection.FileStream.CopyToAsync(stream);
}
Controller
では、単にRequest.Form.Files
ファイルにアクセスするには:
[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();
}