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

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

问题描述:

当我将大文件上传到ASP.NET Core中的Web api时,运行时将在将我的用于处理和存储上载的函数触发之前将文件加载到内存中.对于较大的上载,由于速度慢且需要更多的内存,因此这成为一个问题.对于ASP.NET的早期版本有一些文章关于如何禁用请求缓冲,但是我找不到有关如何使用ASP.NET Core进行此操作的任何信息.是否可以禁用请求缓冲,这样我就不会一直用完服务器上的内存了?

When I'm uploading large files to my web api in ASP.NET Core, the runtime will load the file into memory before my function for processing and storing the upload is fired. With large uploads this becomes an issue as it is both slow and requires more memory. For previous versions of ASP.NET there are some articles on how to disable the buffering of requests, but I'm not able to find any information on how to do this with ASP.NET Core. Is it possible to disable the buffering of requests so I don't run out of memory on my server all the time?

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

可以使用最少的缓冲来解析任何流.它一次为您提供每个节的标题和正文,然后您可以对该节的正文进行所需的操作(缓冲,丢弃,写入磁盘等).

can parse any stream [with] minimal buffering. It gives you the headers and body of each section one at a time and then you do what you want with the body of that section (buffer, discard, write to disk, etc.).

这是一个中间件示例.

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

外部参考

  • https://github.com/aspnet/HttpAbstractions/pull/146
  • https://github.com/aspnet/HttpAbstractions