web-dev-qa-db-fra.com

Comment utiliser IFormFile comme propriété lors du téléchargement d'un fichier sur un serveur à l'aide du framework Asp.NET Core 3.1?

J'essaie de créer une API Web qui gérerait le stockage des fichiers.

Le framework Asp.Net core 1.0+ est livré avec IFormFile interface qui permet de lier le fichier à un modèle de vue. Le documentation sur le téléchargement de fichiers dans ASP.NET Core indique ce qui suit

IFormFile peut être utilisé directement comme paramètre de méthode d'action ou comme propriété de modèle liée.

Lorsque j'ai utilisé IFormFile comme paramètre de méthode d'action, cela fonctionnait sans problème. Mais dans mon cas, je souhaite l'utiliser comme propriété sur un modèle car je voudrais lier d'autres valeurs en plus d'inclure des règles de validation personnalisées. Voici mon modèle de vue.

public class NewFile
{
    [Required]
    [MinFileSize(125), MaxFileSize(5 * 1024 * 1024)]
    [AllowedExtensions(new[] { ".jpg", ".png", ".gif", ".jpeg", ".tiff" })]
    public IFormFile File { get; set; }

    [Required]
    public int? CustomField1 { get; set; }

    [Required]
    public int? CustomField2 { get; set; }

    [Required]
    public int? CustomField3 { get; set; }
}

Voici mon code pour la demande client et le code serveur qui accepte le fichier. Les deux méthodes sont placées dans le même contrôleur par souci de simplicité. Mais en réalité, la méthode "client" sera placée dans une application séparée qui envoie les fichiers.

[ApiController, Route("api/[controller]")]
public class FilesController : ControllerBase
{
    [HttpGet("client")]
    public async Task<IActionResult> Client()
    {
        using HttpClient client = new HttpClient();

        // we need to send a request with multipart/form-data
        var multiForm = new MultipartFormDataContent
        {
            // add API method parameters
            { new StringContent("CustomField1"), "1" },
            { new StringContent("CustomField2"), "1234" },
            { new StringContent("CustomField3"), "5" },
        };

        // add file and directly upload it
        using FileStream fs = System.IO.File.OpenRead("C:/1.jpg");
        multiForm.Add(new StreamContent(fs), "file", "1.jpg");

        // send request to API
        var responce = await client.PostAsync("https://localhost:123/api/files/store", multiForm);

        return Content("Done");
    }

    [HttpPost("store")]
    public async Task<IActionResult> Store(NewFile model)
    {
        if (ModelState.IsValid)
        {
            try
            {
                var filename = MakeFileName(model, Path.GetFileName(model.File.FileName));

                Directory.CreateDirectory(Path.GetDirectoryName(filename));

                using var stream = new FileStream(filename, FileMode.Create);
                await model.File.CopyToAsync(stream);

                return PhysicalFile(filename, "application/octet-stream");
            }
            catch (Exception e)
            {
                return Problem(e.Message);
            }
        }

        // Are there a better way to display validation errors when using Web API?
        var errors = string.Join("; ", ModelState.Values.SelectMany(v => v.Errors).Select(v => v.ErrorMessage));

        return Problem(errors);
    }
}

Lorsque je fais la demande, j'obtiens l'erreur suivante mais la demande ne parvient jamais à la méthode store car j'ai placé un point d'arrêt là-bas, mais elle n'y parvient jamais.

StatusCode: 415, ReasonPhrase: 'Unsupported Media Type', Version: 1.1, Contenu: System.Net.Http.HttpConnectionResponseContent

Comment puis-je envoyer correctement le fichier au serveur et le lier à la propriété File sur mon modèle de vue?

5
user12310517

ApiController par défaut attend JSON sauf indication contraire explicite

Utilisation [FromForm] pour lier le modèle en utilisant des données de formulaire dans le corps de la requête.

public async Task<IActionResult> Store([FromForm]NewFile model) {
    //...
}. 

Référence Liaison de modèle dans ASP.NET Core

les CustomField1, CustomField2 et CustomField3` sont nuls même s'ils sont envoyés comme vous le voyez dans ma question d'origine

le client n'envoie pas correctement ces autres champs. Vous avez changé le contenu et les noms de champ

var multiForm = new MultipartFormDataContent {
    // add API method parameters
    { new StringContent("1"), "CustomField1" },
    { new StringContent("1234"), "CustomField2" },
    { new StringContent("5"), "CustomField3" },
};
4
Nkosi