如何在使用Asp.NET Core 3.1框架将文件上传到服务器时,将IFormFile用作属性?

11

我正在尝试创建一个Web API,用于处理文件存储。

Asp.Net core 1.0+框架提供了IFormFile接口,允许将文件绑定到视图模型上。关于在ASP.NET Core中上传文件的文档指出如下:

IFormFile可以直接用作操作方法参数或绑定模型属性。

当我将IFormFile用作操作方法的参数时,它可以正常工作。但在我的情况下,我希望将其用作模型上的属性,因为我想绑定其他值并包含自定义验证规则。以下是我的视图模型。

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; }
}

这是我处理客户端请求和接受文件的服务器代码。为了简单起见,这两种方法都放在同一控制器中。但实际上,“客户端”方法将被放置在一个单独的应用程序中,用于发送文件。

[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);
    }
}
当我发出请求时,我收到以下错误,但是该请求从未到达store方法,因为我在那里放了一个断点,但它从未到达那里。

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

如何正确地将文件发送到服务器并将其绑定到我的视图模型上的File属性?

1
使用[FromForm]绑定模型public async Task<IActionResult> Store([FromForm]NewFile model)。默认情况下,ApiController期望JSON,除非另有说明。 - Nkosi
1个回答

6

ApiController默认情况下期望JSON,除非明确指定。

使用[FromForm]将模型绑定到请求体中的表单数据。

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

参考 ASP.NET Core 中的模型绑定

即使在原问题中看到了 CustomField1、CustomField2 和 CustomField3,它们仍然是 null。

客户端没有正确发送这些其他字段。您将内容和字段名称混淆了。

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

我按照你的建议添加了[FromForm],但仍然没有起作用。我从控制器中删除了[ApiController]属性,并且请求成功到达了Store方法。然而,即使在原始问题中可以看到它们被发送,CustomField1CustomField2CustomField3仍然为空。 - user12310517
1
@John,客户端没有正确发送那些其他字段。你把内容和名称搞反了。 - Nkosi
2
我该如何在Postman或Fiddler中测试这种类型的API?你能给些建议吗? - Subhasish

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接