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 = ;
});
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 = ;//写出缓冲区大小
int writeCount = ;
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 = ;
while ((readCount = await stream.ReadAsync(byteArr, , byteArr.Length)) > )
{
await fileStream.WriteAsync(byteArr, , readCount);
writeCount += readCount;
}
}
return writeCount;
} }
}

总结:

如果你部署 在iis上或者Nginx 等其他应用服务器 也是需要注意的事情,因为他们本身也有对请求体的限制,还有值得注意的就是我们在创建文件流对象时 缓冲区的大小尽量不要超过netcore大对象的限制。这样在并发高的时候很容易触发二代GC的回收.

NetCore3.0 文件上传与大文件上传的限制的更多相关文章

  1. 框架基础:ajax设计方案(三)--- 集成ajax上传技术 大文件/超大文件前端切割上传,后端进行重组

    马上要过年了,哎,回家的心情也特别的激烈.有钱没钱,回家过年,家永远是舔舐伤口最好的地方.新的一年继续加油努力. 上次做了前端的ajax的上传文件技术,支持单文件,多文件上传,并对文件的格式和大小进行 ...

  2. 前端通信:ajax设计方案(四)--- 集成ajax上传技术 大文件/超大文件前端切割上传,后端进行重组

    马上要过年了,哎,回家的心情也特别的激烈.有钱没钱,回家过年,家永远是舔舐伤口最好的地方.新的一年继续加油努力. 上次做了前端的ajax的上传文件技术,支持单文件,多文件上传,并对文件的格式和大小进行 ...

  3. Loadrunner上传文件解决办法(大文件)

    Loadrunner上传文件解决办法(大文件) 最近再做一个跟海量存储相关的项目测试,需要通过LR模拟用户大量上传和下载文件,请求是Rest或Soap,同时还要模拟多种大小尺寸不一的文件 通常情况下, ...

  4. SC || 解决在git中上传过大文件的问题(如何将提交过的彻底删除

    就在我在ddl前续命的时候……不知道怎么想不开,把v2的压力测试的日志(500多M)也往github上传 之前听说过好多因为传了大文件的锅…… 我竟然还想不开的往上传…… 真实又傻又蠢又自闭(T T ...

  5. Python组织文件 实践:查找大文件、 用Mb、kb显示文件尺寸 、计算程序运行时间

    这个小程序很简单原本没有记录下来的必要,但在编写过程中又让我学到了一些新的知识,并且遇到了一些不能解决的问题,然后,然后就很有必要记录一下. 这个程序的关键是获取文件大小,本来用 os.path.ge ...

  6. 转:Loadrunner上传文件解决办法(大文件)

    最近再做一个跟海量存储相关的项目测试,需要通过LR模拟用户大量上传和下载文件,请求是Rest或Soap,同时还要模拟多种大小尺寸不一的文件 通常情况下,都是使用简单的post协议即可: 方法一: we ...

  7. B/S结构下上传下载大文件(1G以上)的解决方案

    以ASP.NET Core WebAPI 作后端 API ,用 Vue 构建前端页面,用 Axios 从前端访问后端 API ,包括文件的上传和下载. 准备文件上传的API #region 文件上传  ...

  8. ASP.NET 使用ajaxfileupload.js插件出现上传较大文件失败的解决方法(ajaxfileupload.js第一弹)

    在写这篇的时候本来想把标题直接写成报错的提示,如下: “SecurityError:Blocked a frame with origin "http://localhost:55080&q ...

  9. ASP.NET 使用ajaxupload.js插件出现上传较大文件失败的解决方法

    在网上下载了一个ajaxupload.js插件,用于无刷新上传图片使的,然后就按照demo的例子去运行了一下,上传啊什么的都OK,但是正好上传的示例图片有一个比较大的,4M,5M的样子,然后上传就会报 ...

  10. asp.net 文件上传,大文件上传。

    新建一个asp.net页面,在工具栏里拖入 FileUpload 上传控件.一个按钮 Button  !    !     ! 进入Button事件 //----------------------- ...

随机推荐

  1. 就改了get,却不让我set?——Java内省机制的神奇行为举止一例

    [相关类库]org.apache.commons.beanutils.BeanUtils,提供对Java反射和自省API的包装,其中底层使用到了Java的内省方法.[内省的一般应用形式]通过类Intr ...

  2. c++11::initializer_list

    #include <initializer_list> template <class T> class initializer_list; initializer_list对 ...

  3. python类的__repr__方法

    python3中的类默认是新式类(继承object类). __repr__()是 Python 类中的一个特殊方法,由于 object 类己提供了该方法, 而所有 的 Python 类都是 objec ...

  4. 使用zrender.js绘制体温单(2)

    今天我们来画折线图 效果图 以下为模拟数据 [{"time":19,"text":"入\n院\n19\n时\n11\n分","po ...

  5. The usage of Markdown---杂谈:缩进/换行/分割线/注释/文字颜色

    目录 1. 序言 2. 缩进 3. 换行 4. 分割线 5. 注释 6. 改变文字大小和颜色 更新时间:2019.09.14 1. 序言   今天一口气更新了接近10篇博客(虽然只是将我之前基本码好的 ...

  6. 解决Dubbo 2.7.3版本使用ConfigCenterConfig集成Apollo No Provider found的问题

    Dubbo 2.7.3 集成Apollo 问题描述 Dubbo 2.7.3支持配置中心外部化配置, 因此只需要定义一个ConfigCenterConfig的Bean. @EnableDubbo(sca ...

  7. OptimalSolution(8)--位运算

    一.不用额外变量交换两个整数的值 如果给定整数a和b,用以下三行代码即可交换a和b的值.a = a ^ b; b = a ^ b; a = a ^ b; a = a ^ b :假设a异或b的结果记为c ...

  8. Mysql数据库(十一)事务与锁机制

    一.事务机制 1.事务的概念 事务是指一组互相依赖的操作单元的集合,用来保证对数据库的正确修改,保持数据的完整性,如果一个事务的某个单元操作失败,将取消本次事务的全部操作. 比如将A账户的资金转入B账 ...

  9. Unity 单例模式

    明天十一放假,今天不知什么原因看到一篇unity单例模式的介绍,瞬间来了戾气. (一)最简单的单利 public class WebRequestUtility : MonoBehaviour { p ...

  10. LeetCode刷题总结-数组篇(上)

    数组是算法中最常用的一种数据结构,也是面试中最常考的考点.在LeetCode题库中,标记为数组类型的习题到目前为止,已累计到了202题.然而,这202道习题并不是每道题只标记为数组一个考点,大部分习题 ...