处理ASP.NET Core 1.0上的大型文件上载

Mar*_*tin 31 c# asp.net asp.net-mvc asp.net-core-mvc asp.net-core

当我在ASP.NET Core中将大文件上传到我的web api时,运行时会在我处理和存储上传的函数被触发之前将文件加载到内存中.对于大型上传,这会成为一个问题,因为它既缓慢又需要更多内存.对于以前版本的ASP.NET ,有一些关于如何禁用缓冲请求的文章,但是我无法找到有关如何使用ASP.NET Core执行此操作的任何信息.是否可以禁用缓冲请求,以便我的服务器上的内存不会一直耗尽?

Sha*_*tin 25

使用Microsoft.AspNetCore.WebUtilities.MultipartReader因为它......

可以解析任何流[带]最小缓冲.它一次一个地为您提供每个部分的标题和正文,然后您可以使用该部分的主体(缓冲区,丢弃,写入磁盘等)执行您想要的操作.

这是一个中间件示例.

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.");
});
Run Code Online (Sandbox Code Playgroud)

这是帮助者.

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('"');
}
Run Code Online (Sandbox Code Playgroud)

外部参考


Tra*_*yer 5

Shaun Luttin的答案很好,现在他所演示的许多工作都由ASP.Net Core 2.2提供

获取边界:

// Microsoft.AspNetCore.Http.Extensions.HttpRequestMultipartExtensions
var boundary = Request.GetMultipartBoundary();

if (string.IsNullOrWhiteSpace(boundary))
  return BadRequest();
Run Code Online (Sandbox Code Playgroud)

您仍然可以看到以下部分:

var reader = new MultipartReader(boundary, Request.Body);
var section = await reader.ReadNextSectionAsync();
Run Code Online (Sandbox Code Playgroud)

检查配置并转换为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);
}
Run Code Online (Sandbox Code Playgroud)

  • 编辑器在这一行标记错误`if (section.GetContentDispositionHeader())` _Cannot convert ContentDepositionHeaderValue to bool_ (2认同)