web-dev-qa-db-fra.com

Gérer les téléchargements de fichiers volumineux sur ASP.NET Core 1.0

Lorsque je télécharge des fichiers volumineux sur mon API Web dans ASP.NET Core, le runtime charge le fichier en mémoire avant le déclenchement de ma fonction de traitement et de stockage du téléchargement. Avec des téléchargements volumineux, cela devient un problème car il est à la fois lent et nécessite plus de mémoire. Pour les versions précédentes d'ASP.NET il y a quelques articles sur la façon de désactiver la mise en mémoire tampon des requêtes, mais je ne trouve aucune information sur la façon de le faire avec ASP.NET Core. Est-il possible de désactiver la mise en mémoire tampon des requêtes pour ne pas manquer de mémoire sur mon serveur tout le temps?

30
Martin

Utilisez le Microsoft.AspNetCore.WebUtilities.MultipartReader parce qu'il...

peut analyser n'importe quel flux [avec] une mise en mémoire tampon minimale. Il vous donne les en-têtes et le corps de chaque section un par un, puis vous faites ce que vous voulez avec le corps de cette section (tampon, rejet, écriture sur disque, etc.).

Voici un exemple de middleware.

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

Voici les aides.

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

Références externes

24
Shaun Luttin

La réponse de Shaun Luttin est excellente, et maintenant une grande partie du travail qu'il a démontré est fourni par ASP.Net Core 2.2.

Obtenez la frontière:

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

if (string.IsNullOrWhiteSpace(boundary))
  return BadRequest();

Vous obtenez toujours une section comme suit:

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

Vérifiez la disposition et convertissez-la en 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);
}
4
Travis Troyer

Dans votre Controller, vous pouvez simplement utiliser Request.Form.Files pour accéder aux fichiers:

[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();
}
2
Lukazoid