NetCore 3.0文件上傳和大文件上傳的限制詳解
NetCore文件上傳兩種方式
NetCore官方給出的兩種文件上傳方式分別為“緩沖”、“流式”。我簡單的說說兩種的區(qū)別,
1.緩沖:通過模型綁定先把整個文件保存到內(nèi)存,然后我們通過IFormFile得到stream,優(yōu)點(diǎn)是效率高,缺點(diǎn)對內(nèi)存要求大。文件不宜過大。
2.流式處理:直接讀取請求體裝載后的Section 對應(yīng)的stream 直接操作strem即可。無需把整個請求體讀入內(nèi)存,
以下為官方微軟說法
緩沖
整個文件讀入 IFormFile,它是文件的 C# 表示形式,用于處理或保存文件。 文件上傳所用的資源(磁盤、內(nèi)存)取決于并發(fā)文件上傳的數(shù)量和大小。 如果應(yīng)用嘗試緩沖過多上傳,站點(diǎn)就會在內(nèi)存或磁盤空間不足時崩潰。 如果文件上傳的大小或頻率會消耗應(yīng)用資源,請使用流式傳輸。
流式處理
從多部分請求收到文件,然后應(yīng)用直接處理或保存它。 流式傳輸無法顯著提高性能。 流式傳輸可降低上傳文件時對內(nèi)存或磁盤空間的需求。
文件大小限制
說起大小限制,我們得從兩方面入手,1應(yīng)用服務(wù)器Kestrel 2.應(yīng)用程序(我們的netcore程序),
1.應(yīng)用服務(wù)器Kestre設(shè)置
應(yīng)用服務(wù)器Kestrel對我們的限制主要是對整個請求體大小的限制通過如下配置可以進(jìn)行設(shè)置(Program -> CreateHostBuilder),超出設(shè)置范圍會報 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è)置范圍會報InvalidDataException 異常信息
services.Configure<FormOptions>(options =>
{
options.MultipartBodyLengthLimit = long.MaxValue;
});
通過設(shè)置即重置文件上傳的大小限制。
源碼分析
這里我主要說一下 MultipartBodyLengthLimit 這個參數(shù)他主要限制我們使用“緩沖”形式上傳文件時每個的長度。為什么說是緩沖形式中,是因?yàn)槲覀兙彌_形式在讀取上傳文件用的幫助類為 MultipartReaderStream 類下的 Read 方法,此方法在每讀取一次后會更新下讀入的總byte數(shù)量,當(dāng)超過此數(shù)量時會拋出 throw new InvalidDataException($"Multipart body length limit {LengthLimit.GetValueOrDefault()} exceeded."); 主要體現(xiàn)在 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;
}
通過代碼我們可以看到 當(dāng)你做了 MultipartBodyLengthLimit 的限制后,在每次讀取后會累計(jì)讀取的總量,當(dāng)讀取總量超出
MultipartBodyLengthLimit 設(shè)定值會拋出 InvalidDataException 異常,
最終我的文件上傳Controller如下
需要注意的是我們創(chuàng)建 MultipartReader 時并未設(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)建文件流對象時 緩沖區(qū)的大小盡量不要超過netcore大對象的限制。這樣在并發(fā)高的時候很容易觸發(fā)二代GC的回收.
好了,以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,謝謝大家對腳本之家的支持。
相關(guān)文章
ASP.NET中 script runat server 的用法
這篇文章主要介紹了ASP.NET中<script runat="server">的用法,以一個較為簡單的例子分析了ASP.NET中<script runat="server">的特點(diǎn)及具體用法,對于asp.net初學(xué)者來說具有一定的參考借鑒價值,需要的朋友可以參考下2014-11-11
ASP.NET Ajax級聯(lián)DropDownList實(shí)現(xiàn)代碼
我想大家對Ajax已耳熟能詳。自web 2.0概念出現(xiàn)以后,提供更好的用戶體驗(yàn)變得越來越重要。那么今天我來講解一下如何使用ASP.NET Ajax和web service構(gòu)建無刷新級聯(lián)DropDownList(以下簡稱為級聯(lián)DDL)。完成的效果圖2008-12-12
Entity Framework使用Code First模式管理數(shù)據(jù)庫
本文詳細(xì)講解了Entity Framework使用Code First模式管理數(shù)據(jù)庫的方法,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-03-03
Repeater事件OnItemCommand取得行內(nèi)控件的方法
這篇文章主要介紹了Repeater事件OnItemCommand取得行內(nèi)控件的方法,有需要的朋友可以參考一下2014-01-01
ASP.NET Core使用JWT認(rèn)證授權(quán)的方法
這篇文章主要介紹了ASP.NET Core使用JWT認(rèn)證授權(quán)的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11
ASP.NET中實(shí)現(xiàn)文件的保護(hù)性下載基礎(chǔ)篇
許多時候,我們需要在因特網(wǎng)上提供文件下載服務(wù),但是又要防止未經(jīng)授權(quán)的下載,這時該怎么辦?本文將為讀者詳細(xì)介紹一種使用ASP.NET實(shí)現(xiàn)的HTTP處理程序的解決方案。2011-02-02
.NET Core API之格式化輸出對象OutputFormatter
這篇文章介紹了.NET Core API之格式化輸出對象OutputFormatter,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-04-04
ASP.NET WebService中使用ASP.NET_SessionId的問題說明
proxy.CookieContainer存儲了客戶端的 ASP.NET_SessionId。這樣以后每次通過webservice 方法調(diào)用時,都會將ASP.NET_SessionId傳遞到服務(wù)器端。2011-09-09

