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

Posted

技术标签:

【中文标题】使用 Asp.NET Core 3.1 框架将文件上传到服务器时如何使用 IFormFile 作为属性?【英文标题】:How to use IFormFile as property when uploading file to a server using Asp.NET Core 3.1 framework? 【发布时间】:2020-04-19 14:26:17 【问题描述】:

我正在尝试创建一个可以处理存储文件的 Web API。

Asp.Net core 1.0+ 框架附带IFormFile 接口,允许将文件绑定到视图模型。 documentation about uploading files in 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:“不支持的媒体类型”,版本:1.1, 内容:System.Net.Http.HttpConnectionResponseContent

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

【问题讨论】:

使用[FromForm]绑定模型public async Task&lt;IActionResult&gt; Store([FromForm]NewFile model)ApiController 默认情况下需要 JSON,除非另有明确说明 【参考方案1】:

ApiController 默认情况下需要 JSON,除非另有明确说明

使用[FromForm]在请求正文中使用表单数据绑定模型。

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

参考Model Binding in 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 方法。但是,CustomField1、CustomField2, and CustomField3` 为空,即使它们正在发送,正如您在我原来的问题中看到的那样。 @John 客户端未正确发送其他字段。您已切换内容和名称 如何在 postman 或 Fiddler 中测试这种类型的 API?你能建议吗?

以上是关于使用 Asp.NET Core 3.1 框架将文件上传到服务器时如何使用 IFormFile 作为属性?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Visual Studio 2019 在 Docker 容器中运行 ASP.NET Core 3.1 项目?

在 ASP.NET Core 3.1 中上传和下载大文件?

创建基于ASP.NET core 3.1 的RazorPagesMovie项目-应用模型类配合基架生成工具生成Razor页面

Json 对象 Asp.net Core 3.1 的最大长度

如何使用 ASP.NET Core 3.1 将 Android 应用程序集成到 Identity Server 以进行身份​​验证?

Blazor ASP.Net Core 3.1 Web API 在发布时返回 404 错误