Dealing with large file uploads on ASP.NET Core 1.0

前端 未结 3 479
闹比i
闹比i 2020-12-04 17:52

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.

相关标签:
3条回答
  • 2020-12-04 18:32

    Use the Microsoft.AspNetCore.WebUtilities.MultipartReader because it...

    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.).

    Here is a middleware example.

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

    Here are the helpers.

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

    External References

    • https://github.com/aspnet/HttpAbstractions/pull/146
    • https://github.com/aspnet/HttpAbstractions
    0 讨论(0)
  • 2020-12-04 18:46

    Shaun Luttin's answer is great, and now much of the work he's demonstrated is provided by ASP.Net Core 2.2.

    Get the boundary:

    // Microsoft.AspNetCore.Http.Extensions.HttpRequestMultipartExtensions
    var boundary = Request.GetMultipartBoundary();
    
    if (string.IsNullOrWhiteSpace(boundary))
      return BadRequest();
    

    You still get a section as follows:

    var reader = new MultipartReader(boundary, Request.Body);
    var section = await reader.ReadNextSectionAsync();
    

    Check the disposition and convert to 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);
    }
    
    0 讨论(0)
  • 2020-12-04 18:57

    In your Controller you can simply use Request.Form.Files to access the 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();
    }
    
    0 讨论(0)
提交回复
热议问题