ASP.NET Core文件上传IFormFile于Request.Body的羁绊

Posted yi念之间

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ASP.NET Core文件上传IFormFile于Request.Body的羁绊相关的知识,希望对你有一定的参考价值。

前言

    在上篇文章深入探究ASP.NET Core读取Request.Body的正确方式中我们探讨了很多人在日常开发中经常遇到的也是最基础的问题,那就是关于Request.Body的读取方式问题,看是简单实则很容易用不好。笔者也是非常荣幸的得到了许多同学的点赞支持,心理也是非常的兴奋。在此期间在技术交流群中,有一位同学看到了我的文章之后提出了一个疑问,说关于ASP.NET Core文件上传IFormFile和Request.Body之间存在什么样的关系。由于笔者没对这方面有过相关的探究,也没敢做过多回答,怕误导了那位同学,因此私下自己研究了一番,故作此文,希望能帮助更多的同学解除心中的疑惑。

IFormFile的使用方式

考虑到可能有的同学对ASP.NET Core文件上传操作可能不是特别的理解,接下来咱们通过几个简单的操作,让大家简单的熟悉一下。

简单使用演示

首先是最简单的单个文件上传的方式

[HttpPost]
public string UploadFile (IFormFile formFile)
{
    return $"{formFile.FileName}--{formFile.Length}--{formFile.ContentDisposition}--{formFile.ContentType}";
}

非常简单的操作,通过IFormFile实例直接获取文件信息,这里需要注意模型绑定的名称一定要和提交的表单值的name保持一致,这样才能正确的完成模型绑定。还有的时候我们是要通过一个接口完成一批文件上传,这个时候我们可以使用下面的方式

[HttpPost]
public IEnumerable<string> UploadFiles(List<IFormFile> formFiles)
{
    return formFiles.Select(i => $"{i.FileName}--{ i.Length}-{ i.ContentDisposition}--{ i.ContentType}");
}

直接将模型绑定的参数声明为集合类型即可,同时也需要注意模型绑定的名称和上传文件form的name要保持一致。不过有的时候你可能连List这种集合类型也不想写,想通过一个类就能得到上传的文件集合,好在微软够贴心,给我们提供了另一个类,操作如下

[HttpPost]
public IEnumerable<string> UploadFiles3(IFormFileCollection formFiles)
{
    return formFiles.Select(i => $"{i.FileName}--{ i.Length}-{ i.ContentDisposition}--{ i.ContentType}");
}

对微软的代码风格有了解的同学看到名字就知道,IFormFileCollection其实也是对IFormFile集合的封装。有时候你可能都不想使用IFormFile的相关模型绑定,可能是你怕记不住这个名字,那还有别的方式能操作上传文件吗?当然有,可以直接在Request表单中获取上传文件信息

[HttpPost]
public IEnumerable<string> UploadFiles2()
{
    IFormFileCollection formFiles = Request.Form.Files;
    return formFiles.Select(i => $"{i.FileName}--{ i.Length}-{ i.ContentDisposition}--{ i.ContentType}");
}

其实它的本质也是获取到IFormFileCollection,不过这种方式更加的灵活。首先是不需要模型绑定名称不一致的问题,其次是只要有Request的地方就可以获取到上传的文件信息。

操作上传内容

如果你想保存上传的文件,或者是直接读取上传的文件信息,IFormFile为我们提供两种可以操作上传文件内容信息的方式

  • 一种是将上传文件的Stream信息Copy到一个新的Stream中
  • 另一种是直接通过OpenReadStream的方式直接获取上传文件的Stream信息

两种操作方式大致如下

[HttpPost]
public async Task<string> UploadFile (IFormFile formFile)
{
    if (formFile.Length > 0)
    {
        //1.使用CopyToAsync的方式
        using var stream = System.IO.File.Create("test.txt");
        await formFile.CopyToAsync(stream);

        //2.使用OpenReadStream的方式直接得到上传文件的Stream
        StreamReader streamReader = new StreamReader(formFile.OpenReadStream());
        string content = streamReader.ReadToEnd();
    }
    return $"{formFile.FileName}--{formFile.Length}--{formFile.ContentDisposition}--{formFile.ContentType}";
}
更改内容大小限制

ASP.NET Core会对上传文件的大小做出一定的限制,默认限制大小约是2MB(以字节为单位)左右,如果超出这个限制,会直接抛出异常。如何加下来我们看一下如何修改上传文件的大小限制通过ConfigureServices的方式直接配置FormOptions的MultipartBodyLengthLimit

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<FormOptions>(options =>
    {
        // 设置上传大小限制256MB
        options.MultipartBodyLengthLimit = 268435456;
    });
}

这里只是修改了对上传文件主题大小的限制,熟悉ASP.NET Core的同学可能知道,默认情况下Kestrel对Request的Body大小也有限制,这时候我们还需要对Kestrel的RequestBody大小进行修改,操作如下所示

public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.ConfigureKestrel((context, options) =>
                    {
                        //设置Body大小限制256MB
                        options.Limits.MaxRequestBodySize = 268435456;
                    });
                    webBuilder.UseStartup<Startup>();
                });

很多时候这两处设置都需要配合着一起使用,才能达到效果,用的时候需要特别的留意一下。

源码探究

上面我们大致演示了IFormFile的基础操作,我们上面的演示大致划分为两类,一种是通过模型绑定的方式而这种方式包含了IFormFileList<IFormFile>IFormFileCollection三种方式 ,另一种是通过Request.Form.Files的方式,为了搞懂他们的关系,就必须从模型绑定下手。

始于模型绑定

首先我们找到关于操作FormFile相关操作模型绑定的地方在FormFileModelBinder类的BindModelAsync方法[点击查看源码

以上是关于ASP.NET Core文件上传IFormFile于Request.Body的羁绊的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET Core文件上传IFormFile于Request.Body的羁绊

如何将图像从 Asp.net Core IFormFile 上传到 Azure Blob 存储?

在 ASP.NET Core 中上传文件

IFormFile 始终为空(带有 MVC/Razor 的 ASP.NET Core)

ASP.Net Core 对路径的访问被 IFormFile 拒绝

使用 ASP.NET Core Web API 将图像上传到 Cloudianary 时出现空文件错误