NetCore 3.0文件上传和大文件上传的限制详解
NetCore文件上传两种方式
NetCore官方给出的两种文件上传方式分别为“缓冲”、“流式”。我简单的说说两种的区别,
1.缓冲:通过模型绑定先把整个文件保存到内存,然后我们通过IFormFile得到stream,优点是效率高,缺点对内存要求大。文件不宜过大。
2.流式处理:直接读取请求体装载后的Section 对应的stream 直接操作strem即可。无需把整个请求体读入内存,
以下为官方微软说法
缓冲
整个文件读入 IFormFile,它是文件的 C# 表示形式,用于处理或保存文件。 文件上传所用的资源(磁盘、内存)取决于并发文件上传的数量和大小。 如果应用尝试缓冲过多上传,站点就会在内存或磁盘空间不足时崩溃。 如果文件上传的大小或频率会消耗应用资源,请使用流式传输。
流式处理
从多部分请求收到文件,然后应用直接处理或保存它。 流式传输无法显著提高性能。 流式传输可降低上传文件时对内存或磁盘空间的需求。
文件大小限制
说起大小限制,我们得从两方面入手,1应用服务器Kestrel 2.应用程序(我们的netcore程序),
1.应用服务器Kestre设置
应用服务器Kestrel对我们的限制主要是对整个请求体大小的限制通过如下配置可以进行设置(Program -> CreateHostBuilder),超出设置范围会报 BadHttpRequestException: Request body too large
异常信息
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.ConfigureKestrel((context, options) => { //设置应用服务器Kestrel请求体最大为50MB options.Limits.MaxRequestBodySize = 52428800; }); webBuilder.UseStartup<Startup>(); });
2.应用程序设置
应用程序设置 (Startup-> ConfigureServices) 超出设置范围会报InvalidDataException 异常信息
services.Configure<FormOptions>(options => { options.MultipartBodyLengthLimit = long.MaxValue; });
通过设置即重置文件上传的大小限制。
源码分析
这里我主要说一下 MultipartBodyLengthLimit 这个参数他主要限制我们使用“缓冲”形式上传文件时每个的长度。为什么说是缓冲形式中,是因为我们缓冲形式在读取上传文件用的帮助类为 MultipartReaderStream 类下的 Read 方法,此方法在每读取一次后会更新下读入的总byte数量,当超过此数量时会抛出 throw new InvalidDataException($"Multipart body length limit {LengthLimit.GetValueOrDefault()} exceeded.");
主要体现在 UpdatePosition 方法对 _observedLength 的判断
以下为 MultipartReaderStream 类两个方法的源代码,为方便阅读,我已精简掉部分代码
Read
public override int Read(byte[] buffer, int offset, int count) { var bufferedData = _innerStream.BufferedData; int read; read = _innerStream.Read(buffer, offset, Math.Min(count, bufferedData.Count)); return UpdatePosition(read); }
UpdatePosition
private int UpdatePosition(int read) { _position += read; if (_observedLength < _position) { _observedLength = _position; if (LengthLimit.HasValue && _observedLength > LengthLimit.GetValueOrDefault()) { throw new InvalidDataException($"Multipart body length limit {LengthLimit.GetValueOrDefault()} exceeded."); } } return read; }
通过代码我们可以看到 当你做了 MultipartBodyLengthLimit 的限制后,在每次读取后会累计读取的总量,当读取总量超出
MultipartBodyLengthLimit 设定值会抛出 InvalidDataException 异常,
最终我的文件上传Controller如下
需要注意的是我们创建 MultipartReader 时并未设置 BodyLengthLimit (这参数会传给 MultipartReaderStream.LengthLimit
)也就是我们最终的限制,这里我未设置值也就无限制,可以通过 UpdatePosition 方法体现出来
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.WebUtilities; using Microsoft.Net.Http.Headers; using System.IO; using System.Threading.Tasks; namespace BigFilesUpload.Controllers { [Route("api/[controller]")] public class FileController : Controller { private readonly string _targetFilePath = "C:\\files\\TempDir"; /// <summary> /// 流式文件上传 /// </summary> /// <returns></returns> [HttpPost("UploadingStream")] public async Task<IActionResult> UploadingStream() { //获取boundary var boundary = HeaderUtilities.RemoveQuotes(MediaTypeHeaderValue.Parse(Request.ContentType).Boundary).Value; //得到reader var reader = new MultipartReader(boundary, HttpContext.Request.Body); //{ BodyLengthLimit = 2000 };// var section = await reader.ReadNextSectionAsync(); //读取section while (section != null) { var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition); if (hasContentDispositionHeader) { var trustedFileNameForFileStorage = Path.GetRandomFileName(); await WriteFileAsync(section.Body, Path.Combine(_targetFilePath, trustedFileNameForFileStorage)); } section = await reader.ReadNextSectionAsync(); } return Created(nameof(FileController), null); } /// <summary> /// 缓存式文件上传 /// </summary> /// <param name=""></param> /// <returns></returns> [HttpPost("UploadingFormFile")] public async Task<IActionResult> UploadingFormFile(IFormFile file) { using (var stream = file.OpenReadStream()) { var trustedFileNameForFileStorage = Path.GetRandomFileName(); await WriteFileAsync(stream, Path.Combine(_targetFilePath, trustedFileNameForFileStorage)); } return Created(nameof(FileController), null); } /// <summary> /// 写文件导到磁盘 /// </summary> /// <param name="stream">流</param> /// <param name="path">文件保存路径</param> /// <returns></returns> public static async Task<int> WriteFileAsync(System.IO.Stream stream, string path) { const int FILE_WRITE_SIZE = 84975;//写出缓冲区大小 int writeCount = 0; using (FileStream fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Write, FILE_WRITE_SIZE, true)) { byte[] byteArr = new byte[FILE_WRITE_SIZE]; int readCount = 0; while ((readCount = await stream.ReadAsync(byteArr, 0, byteArr.Length)) > 0) { await fileStream.WriteAsync(byteArr, 0, readCount); writeCount += readCount; } } return writeCount; } } }
总结:
如果你部署 在iis上或者Nginx 等其他应用服务器 也是需要注意的事情,因为他们本身也有对请求体的限制,还有值得注意的就是我们在创建文件流对象时 缓冲区的大小尽量不要超过netcore大对象的限制。这样在并发高的时候很容易触发二代GC的回收.
好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对我们的支持。
上一篇:详解在ASP.NET Core中如何编写合格的中间件
栏 目:ASP.NET
下一篇:ASP.NET Core 过滤器中使用依赖注入知识点总结
本文标题:NetCore 3.0文件上传和大文件上传的限制详解
本文地址:https://www.xiuzhanwang.com/a1/ASP_NET/10858.html
您可能感兴趣的文章
- 01-11ASP.NET Core静态文件的使用方法
- 01-11.NET Core 3.0之创建基于Consul的Configuration扩展组件
- 01-11asp.net Core3.0区域与路由配置的方法
- 01-11.NET Core 3.0 可回收程序集加载上下文的实现
- 01-11.NET CORE中比较两个文件内容是否相同的最快方法
- 01-11使用dotnet-dump 查找 .net core 3.0 占用CPU 100%的原因解析
- 01-11.NET Core 3.0中WPF使用IOC的图文教程
- 01-11vs2015中mysql.h文件打不开的解决办法
- 01-11aspnetcore 实现简单的伪静态化功能
- 01-11asp.net大文件上传解决方案实例代码
阅读排行
本栏相关
- 01-11vscode extension插件开发详解
- 01-11VsCode插件开发之插件初步通信的方法
- 01-11如何给asp.net core写个简单的健康检查
- 01-11.net core高吞吐远程方法如何调用组件
- 01-11浅析.Net Core中Json配置的自动更新
- 01-11.NET开发人员关于ML.NET的入门学习
- 01-11.NET Core 迁移躺坑记续集之Win下莫名其
- 01-11.net core webapi jwt 更为清爽的认证详解
- 01-11docker部署Asp.net core应用的完整步骤
- 01-11ASP.NET Core静态文件的使用方法
随机阅读
- 01-11ajax实现页面的局部加载
- 04-02jquery与jsp,用jquery
- 08-05DEDE织梦data目录下的sessions文件夹有什
- 08-05织梦dedecms什么时候用栏目交叉功能?
- 01-10C#中split用法实例总结
- 08-05dedecms(织梦)副栏目数量限制代码修改
- 01-11Mac OSX 打开原生自带读写NTFS功能(图文
- 01-10使用C语言求解扑克牌的顺子及n个骰子
- 01-10delphi制作wav文件的方法
- 01-10SublimeText编译C开发环境设置