NetCore 3.0文件上傳和大文件上傳的限制詳解
NetCore文件上傳兩種方式
NetCore官方給出的兩種文件上傳方式分別為“緩沖”、“流式”。我簡單的說說兩種的區(qū)別,
1.緩沖:通過模型綁定先把整個(gè)文件保存到內(nèi)存,然后我們通過IFormFile得到stream,優(yōu)點(diǎn)是效率高,缺點(diǎn)對內(nèi)存要求大。文件不宜過大。
2.流式處理:直接讀取請求體裝載后的Section 對應(yīng)的stream 直接操作strem即可。無需把整個(gè)請求體讀入內(nèi)存,
以下為官方微軟說法
緩沖
整個(gè)文件讀入 IFormFile,它是文件的 C# 表示形式,用于處理或保存文件。 文件上傳所用的資源(磁盤、內(nèi)存)取決于并發(fā)文件上傳的數(shù)量和大小。 如果應(yīng)用嘗試緩沖過多上傳,站點(diǎn)就會在內(nèi)存或磁盤空間不足時(shí)崩潰。 如果文件上傳的大小或頻率會消耗應(yīng)用資源,請使用流式傳輸。
流式處理
從多部分請求收到文件,然后應(yīng)用直接處理或保存它。 流式傳輸無法顯著提高性能。 流式傳輸可降低上傳文件時(shí)對內(nèi)存或磁盤空間的需求。
文件大小限制
說起大小限制,我們得從兩方面入手,1應(yīng)用服務(wù)器Kestrel 2.應(yīng)用程序(我們的netcore程序),
1.應(yīng)用服務(wù)器Kestre設(shè)置
應(yīng)用服務(wù)器Kestrel對我們的限制主要是對整個(gè)請求體大小的限制通過如下配置可以進(jìn)行設(shè)置(Program -> CreateHostBuilder),超出設(shè)置范圍會報(bào) BadHttpRequestException: Request body too large 異常信息
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureKestrel((context, options) =>
{
//設(shè)置應(yīng)用服務(wù)器Kestrel請求體最大為50MB
options.Limits.MaxRequestBodySize = 52428800;
});
webBuilder.UseStartup<Startup>();
});
2.應(yīng)用程序設(shè)置
應(yīng)用程序設(shè)置 (Startup-> ConfigureServices) 超出設(shè)置范圍會報(bào)InvalidDataException 異常信息
services.Configure<FormOptions>(options =>
{
options.MultipartBodyLengthLimit = long.MaxValue;
});
通過設(shè)置即重置文件上傳的大小限制。
源碼分析
這里我主要說一下 MultipartBodyLengthLimit 這個(gè)參數(shù)他主要限制我們使用“緩沖”形式上傳文件時(shí)每個(gè)的長度。為什么說是緩沖形式中,是因?yàn)槲覀兙彌_形式在讀取上傳文件用的幫助類為 MultipartReaderStream 類下的 Read 方法,此方法在每讀取一次后會更新下讀入的總byte數(shù)量,當(dāng)超過此數(shù)量時(shí)會拋出 throw new InvalidDataException($"Multipart body length limit {LengthLimit.GetValueOrDefault()} exceeded."); 主要體現(xiàn)在 UpdatePosition 方法對 _observedLength 的判斷
以下為 MultipartReaderStream 類兩個(gè)方法的源代碼,為方便閱讀,我已精簡掉部分代碼
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;
}
通過代碼我們可以看到 當(dāng)你做了 MultipartBodyLengthLimit 的限制后,在每次讀取后會累計(jì)讀取的總量,當(dāng)讀取總量超出
MultipartBodyLengthLimit 設(shè)定值會拋出 InvalidDataException 異常,
最終我的文件上傳Controller如下
需要注意的是我們創(chuàng)建 MultipartReader 時(shí)并未設(shè)置 BodyLengthLimit (這參數(shù)會傳給 MultipartReaderStream.LengthLimit )也就是我們最終的限制,這里我未設(shè)置值也就無限制,可以通過 UpdatePosition 方法體現(xiàn)出來
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>
/// 寫文件導(dǎo)到磁盤
/// </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;//寫出緩沖區(qū)大小
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;
}
}
}
總結(jié):
如果你部署 在iis上或者Nginx 等其他應(yīng)用服務(wù)器 也是需要注意的事情,因?yàn)樗麄儽旧硪灿袑φ埱篌w的限制,還有值得注意的就是我們在創(chuàng)建文件流對象時(shí) 緩沖區(qū)的大小盡量不要超過netcore大對象的限制。這樣在并發(fā)高的時(shí)候很容易觸發(fā)二代GC的回收.
好了,以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對我們的支持。
上一篇:詳解在ASP.NET Core中如何編寫合格的中間件
欄 目:ASP.NET
下一篇:ASP.NET Core 過濾器中使用依賴注入知識點(diǎn)總結(jié)
本文標(biāo)題:NetCore 3.0文件上傳和大文件上傳的限制詳解
本文地址:http://www.jygsgssxh.com/a1/ASP_NET/10858.html
您可能感興趣的文章
- 01-11ASP.NET Core靜態(tài)文件的使用方法
- 01-11.NET Core 3.0之創(chuàng)建基于Consul的Configuration擴(kuò)展組件
- 01-11asp.net Core3.0區(qū)域與路由配置的方法
- 01-11.NET Core 3.0 可回收程序集加載上下文的實(shí)現(xiàn)
- 01-11.NET CORE中比較兩個(gè)文件內(nèi)容是否相同的最快方法
- 01-11使用dotnet-dump 查找 .net core 3.0 占用CPU 100%的原因解析
- 01-11.NET Core 3.0中WPF使用IOC的圖文教程
- 01-11vs2015中mysql.h文件打不開的解決辦法
- 01-11aspnetcore 實(shí)現(xiàn)簡單的偽靜態(tài)化功能
- 01-11asp.net大文件上傳解決方案實(shí)例代碼


閱讀排行
本欄相關(guān)
- 01-11vscode extension插件開發(fā)詳解
- 01-11VsCode插件開發(fā)之插件初步通信的方法
- 01-11如何給asp.net core寫個(gè)簡單的健康檢查
- 01-11.net core高吞吐遠(yuǎn)程方法如何調(diào)用組件
- 01-11淺析.Net Core中Json配置的自動更新
- 01-11.NET開發(fā)人員關(guān)于ML.NET的入門學(xué)習(xí)
- 01-11.NET Core 遷移躺坑記續(xù)集之Win下莫名其
- 01-11.net core webapi jwt 更為清爽的認(rèn)證詳解
- 01-11docker部署Asp.net core應(yīng)用的完整步驟
- 01-11ASP.NET Core靜態(tài)文件的使用方法
隨機(jī)閱讀
- 01-11ajax實(shí)現(xiàn)頁面的局部加載
- 04-02jquery與jsp,用jquery
- 08-05DEDE織夢data目錄下的sessions文件夾有什
- 08-05織夢dedecms什么時(shí)候用欄目交叉功能?
- 01-10C#中split用法實(shí)例總結(jié)
- 08-05dedecms(織夢)副欄目數(shù)量限制代碼修改
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 01-10使用C語言求解撲克牌的順子及n個(gè)骰子
- 01-10delphi制作wav文件的方法
- 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置


