MultipartMemoryStream en Dot Net Core

4

Tengo una aplicación .Net 4.5.2 que voy a cambiar a Dot Net Core. Esta aplicación permite al usuario cargar un archivo con metadatos (lado del cliente angular) y la API manejará la solicitud y procesará el archivo. Aquí está el código existente que hace eso.

API

[HttpPost]
[Route("AskQuestions")]
public void ProvideClarifications(int id)
{
  var user = base.GetUserLookup();
  if (user != null)
  {
    var streamProvider = new MultiPartStreamProvider();
    IEnumerable<HttpContent> parts = null;
    Task.Factory
        .StartNew(() => parts = Request.Content.ReadAsMultipartAsync(streamProvider).Result.Contents,
            CancellationToken.None,
            TaskCreationOptions.LongRunning,
            TaskScheduler.Default)
        .Wait();

        // do some stuff with streamProvider.FormData
  }
}

Proveedor para manejar archivos y metadatos

public class MultiPartStreamProvider : MultipartMemoryStreamProvider
{
    private string _originalFileName = string.Empty;
    public Dictionary<string, object> FormData { get; set; }

    public byte[] ByteStream { get; set; }

    public string FileName
    {
        get
        {
            return _originalFileName.Replace("\"", "");
        }
    }
    public MultiPartStreamProvider()
    {
        this.FormData = new Dictionary<string, object>();
    }
    public override Task ExecutePostProcessingAsync()
    {
        foreach (var content in Contents)
        {
            var contentDispo = content.Headers.ContentDisposition;
            var name = UnquoteToken(contentDispo.Name);

            if (name.Contains("file"))
            {
                _originalFileName = UnquoteToken(contentDispo.FileName);
                this.ByteStream = content.ReadAsByteArrayAsync().Result;
            }
            else
            {
                var val = content.ReadAsStringAsync().Result;
                this.FormData.Add(name, val);
            }
        }
        return base.ExecutePostProcessingAsync();
    }
    private static string UnquoteToken(string token)
    {
        if (String.IsNullOrWhiteSpace(token))
        {
            return token;
        }

        if (token.StartsWith("\"", StringComparison.Ordinal) && token.EndsWith("\"", StringComparison.Ordinal) && token.Length > 1)
        {
            return token.Substring(1, token.Length - 2);
        }

        return token;
    }
}
static class FormDataExtensions {
    public static Object GetObject(this Dictionary<string, object> dict, Type type)
    {
        var obj = Activator.CreateInstance(type);

        foreach (var kv in dict)
        {
            var prop = type.GetProperty(kv.Key);
            if (prop == null) continue;

            object value = kv.Value;
            var targetType = IsNullableType(prop.PropertyType) ? Nullable.GetUnderlyingType(prop.PropertyType) : prop.PropertyType;

            if (value is Dictionary<string, object>)
            {
                value = GetObject((Dictionary<string, object>)value, prop.PropertyType); // <= This line                   
            }
            value = Convert.ChangeType(value, targetType);
            prop.SetValue(obj, value, null);
        }
        return obj;
    }
    public static T GetObject<T>(this Dictionary<string, object> dict)
    {
        return (T)GetObject(dict, typeof(T));
    }
    private static bool IsNullableType(Type type)
    {
        return type.IsGenericType && type.GetGenericTypeDefinition().Equals(typeof(Nullable<>));
    }
}

Entonces esto funciona totalmente bien en el marco de orientación, pero en Core obtengo una excepción en esta línea

.StartNew(() => parts = Request.Content.ReadAsMultipartAsync(streamProvider).Result.Contents,

Excepción

'HttpRequest' does not contain a definition for 'Content' and no extension method 'Content' accepting a first argument of type 'HttpRequest' could be found (are you missing a using directive or an assembly reference?)

¿Qué necesito para asegurarme de que puedo obtener este archivo y metadatos? ¿Hay alguna forma en Core de obtener HttpContent de una HttpRequest?

5

Asp.Net Core tiene soporte para cargas de archivos de varias partes integradas. El componente de enlace de modelo lo hará disponible cuando tenga un List<IFormFile>parámetro.

Consulte los documentos sobre cargas de archivos para obtener más detalles, aquí está el ejemplo relevante que brinda para manejar cargas de varias partes:

[HttpPost("UploadFiles")]
public async Task<IActionResult> Post(List<IFormFile> files)
{
    long size = files.Sum(f => f.Length);

    // full path to file in temp location
    var filePath = Path.GetTempFileName();

    foreach (var formFile in files)
    {
        if (formFile.Length > 0)
        {
            using (var stream = new FileStream(filePath, FileMode.Create))
            {
                await formFile.CopyToAsync(stream);
            }
        }
    }

    // process uploaded files
    // Don't rely on or trust the FileName property without validation.

    return Ok(new { count = files.Count, size, filePath});
}
6
  • Gracias por esto, ¿puedo usar algo como Request.Form.Files para obtener esto también? Estoy enviando un archivo junto con los metadatos a los que necesito acceder desde Request.Form 2 de junio de 2017 a las 13:10
  • @MartinUllrich, ¿hay alguna razón por la que está procesando los archivos de forma secuencial sincrónica, en lugar de esperar todas las tareas a la vez? 2 feb 2018 a las 1:59
  • Ah, parece que no hará mucha diferencia actualmente porque todo estará embotellado en el HDD. Pero, ¿por qué está guardando todos los archivos en la misma ubicación? 2 de febrero de 2018 a las 4:18
  • @ johnny5 tienes razón, se ve raro. Esto está destinado a ser una cita de los documentos, la muestra se toma del sitio de documentos vinculado. Me pondré en contacto con uno de los autores de esta página de documentos. 2 feb 2018 a las 6:51
  • 1
    Gracias Martin, solo espero que nadie esté usando esto en el código de producción, pero de nuevo, si es así, deberían haberlo probado mejor. 2 feb 2018 a las 13:34