日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > asp.net >内容正文

asp.net

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

發(fā)布時(shí)間:2023/12/4 asp.net 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ASP.NET Core文件上传IFormFile于Request.Body的羁绊 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

前言

????在上篇文章深入探究ASP.NET Core讀取Request.Body的正確方式[1]中我們探討了很多人在日常開(kāi)發(fā)中經(jīng)常遇到的也是最基礎(chǔ)的問(wèn)題,那就是關(guān)于Request.Body的讀取方式問(wèn)題,看是簡(jiǎn)單實(shí)則很容易用不好。筆者也是非常榮幸的得到了許多同學(xué)的點(diǎn)贊支持,心理也是非常的興奮。在此期間在技術(shù)交流群中,有一位同學(xué)看到了我的文章之后提出了一個(gè)疑問(wèn),說(shuō)關(guān)于ASP.NET Core文件上傳IFormFile和Request.Body之間存在什么樣的關(guān)系。由于筆者沒(méi)對(duì)這方面有過(guò)相關(guān)的探究,也沒(méi)敢做過(guò)多回答,怕誤導(dǎo)了那位同學(xué),因此私下自己研究了一番,故作此文,希望能幫助更多的同學(xué)解除心中的疑惑。

IFormFile的使用方式

考慮到可能有的同學(xué)對(duì)ASP.NET Core文件上傳操作可能不是特別的理解,接下來(lái)咱們通過(guò)幾個(gè)簡(jiǎn)單的操作,讓大家簡(jiǎn)單的熟悉一下。

簡(jiǎn)單使用演示

首先是最簡(jiǎn)單的單個(gè)文件上傳的方式

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

非常簡(jiǎn)單的操作,通過(guò)IFormFile實(shí)例直接獲取文件信息,這里需要注意模型綁定的名稱(chēng)一定要和提交的表單值的name保持一致,這樣才能正確的完成模型綁定。還有的時(shí)候我們是要通過(guò)一個(gè)接口完成一批文件上傳,這個(gè)時(shí)候我們可以使用下面的方式

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

直接將模型綁定的參數(shù)聲明為集合類(lèi)型即可,同時(shí)也需要注意模型綁定的名稱(chēng)和上傳文件form的name要保持一致。不過(guò)有的時(shí)候你可能連List這種集合類(lèi)型也不想寫(xiě),想通過(guò)一個(gè)類(lèi)就能得到上傳的文件集合,好在微軟夠貼心,給我們提供了另一個(gè)類(lèi),操作如下

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

對(duì)微軟的代碼風(fēng)格有了解的同學(xué)看到名字就知道,IFormFileCollection其實(shí)也是對(duì)IFormFile集合的封裝。有時(shí)候你可能都不想使用IFormFile的相關(guān)模型綁定,可能是你怕記不住這個(gè)名字,那還有別的方式能操作上傳文件嗎?當(dāng)然有,可以直接在Request表單中獲取上傳文件信息

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

其實(shí)它的本質(zhì)也是獲取到IFormFileCollection,不過(guò)這種方式更加的靈活。首先是不需要模型綁定名稱(chēng)不一致的問(wèn)題,其次是只要有Request的地方就可以獲取到上傳的文件信息。

操作上傳內(nèi)容

如果你想保存上傳的文件,或者是直接讀取上傳的文件信息,IFormFile為我們提供兩種可以操作上傳文件內(nèi)容信息的方式

?一種是將上傳文件的Stream信息Copy到一個(gè)新的Stream中?另一種是直接通過(guò)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的方式直接得到上傳文件的StreamStreamReader streamReader = new StreamReader(formFile.OpenReadStream());string content = streamReader.ReadToEnd();}return $"{formFile.FileName}--{formFile.Length}--{formFile.ContentDisposition}--{formFile.ContentType}"; }

更改內(nèi)容大小限制

ASP.NET Core會(huì)對(duì)上傳文件的大小做出一定的限制,默認(rèn)限制大小約是2MB(以字節(jié)為單位)左右,如果超出這個(gè)限制,會(huì)直接拋出異常。如何加下來(lái)我們看一下如何修改上傳文件的大小限制通過(guò)ConfigureServices的方式直接配置FormOptions的MultipartBodyLengthLimit

public void ConfigureServices(IServiceCollection services) {services.Configure<FormOptions>(options =>{// 設(shè)置上傳大小限制256MBoptions.MultipartBodyLengthLimit = 268435456;}); }

這里只是修改了對(duì)上傳文件主題大小的限制,熟悉ASP.NET Core的同學(xué)可能知道,默認(rèn)情況下Kestrel對(duì)Request的Body大小也有限制,這時(shí)候我們還需要對(duì)Kestrel的RequestBody大小進(jìn)行修改,操作如下所示

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

很多時(shí)候這兩處設(shè)置都需要配合著一起使用,才能達(dá)到效果,用的時(shí)候需要特別的留意一下。

源碼探究

上面我們大致演示了IFormFile的基礎(chǔ)操作,我們上面的演示大致劃分為兩類(lèi),一種是通過(guò)模型綁定的方式而這種方式包含了IFormFile、List<IFormFile>、IFormFileCollection三種方式 ,另一種是通過(guò)Request.Form.Files的方式,為了搞懂他們的關(guān)系,就必須從模型綁定下手。

始于模型綁定

首先我們找到關(guān)于操作FormFile相關(guān)操作模型綁定的地方在FormFileModelBinder類(lèi)的BindModelAsync方法[點(diǎn)擊查看源碼????[2]]我們看到了如下代碼,展示的代碼刪除了部分邏輯,提取的是涉及到我們要關(guān)注的流程性的操作

public async Task BindModelAsync(ModelBindingContext bindingContext) {//獲取要綁定的參數(shù)類(lèi)型var createFileCollection = bindingContext.ModelType == typeof(IFormFileCollection);//判斷模型綁定參數(shù)類(lèi)型是IFormFileCollection類(lèi)型或可兼容IFormFileCollection類(lèi)型//其中ModelBindingHelper.CanGetCompatibleCollection是用來(lái)判斷模型綁定參數(shù)是否可以兼容IFormFileCollectionif (!createFileCollection && !ModelBindingHelper.CanGetCompatibleCollection<IFormFile>(bindingContext)){return;}//判斷模型綁定參數(shù)是否是集合類(lèi)型ICollection<IFormFile> postedFiles;if (createFileCollection){postedFiles = new List<IFormFile>();}else{//不是集合類(lèi)型的的話,包裝成為集合類(lèi)型//其中ModelBindingHelper.GetCompatibleCollection是將模型綁定參數(shù)綁包裝成集合類(lèi)型postedFiles = ModelBindingHelper.GetCompatibleCollection<IFormFile>(bindingContext);}//獲取要模型綁定的參數(shù)名稱(chēng)var modelName = bindingContext.IsTopLevelObject? bindingContext.BinderModelName ?? bindingContext.FieldName: bindingContext.ModelName;//給postedFiles添加值,postedFiles將承載上傳的所有文件await GetFormFilesAsync(modelName, bindingContext, postedFiles);if (postedFiles.Count == 0 &&bindingContext.OriginalModelName != null &&!string.Equals(modelName, bindingContext.OriginalModelName, StringComparison.Ordinal) &&!modelName.StartsWith(bindingContext.OriginalModelName + "[", StringComparison.Ordinal) &&!modelName.StartsWith(bindingContext.OriginalModelName + ".", StringComparison.Ordinal)){modelName = ModelNames.CreatePropertyModelName(bindingContext.OriginalModelName, modelName);await GetFormFilesAsync(modelName, bindingContext, postedFiles);}object value;//如果模型參數(shù)為IFormFileif (bindingContext.ModelType == typeof(IFormFile)){//并未獲取上傳文件相關(guān)直接返回if (postedFiles.Count == 0){return;}//集合存在則獲取第一個(gè)value = postedFiles.First();}else{//如果模型參數(shù)不為IFormFileif (postedFiles.Count == 0 && !bindingContext.IsTopLevelObject){return;}var modelType = bindingContext.ModelType;//如果模型參數(shù)為IFormFile[]則直接將postedFiles轉(zhuǎn)換為IFormFile[]if (modelType == typeof(IFormFile[])){Debug.Assert(postedFiles is List<IFormFile>);value = ((List<IFormFile>)postedFiles).ToArray();}//如果模型參數(shù)為IFormFileCollection則直接使用postedFiles初始化FileCollectionelse if (modelType == typeof(IFormFileCollection)){Debug.Assert(postedFiles is List<IFormFile>);value = new FileCollection((List<IFormFile>)postedFiles);}//其他類(lèi)型則直接賦值else{value = postedFiles;}}bindingContext.Result = ModelBindingResult.Success(value); }

上面的源碼中涉及到了ModelBindingHelper模型綁定幫助類(lèi)[點(diǎn)擊查看源碼????[3]]相關(guān)的方法,主要是封裝模型綁定公共的幫助類(lèi)。涉及到的我們需要的方法邏輯,上面?zhèn)渥⒁呀?jīng)說(shuō)明了,這里就不展示源碼了,因?yàn)樗鼘?duì)于我們的流程來(lái)說(shuō)并不核心。

上面我們看到了用于初始化綁定集合的核心操作是GetFormFilesAsync方法[點(diǎn)擊查看源碼????[4]]話不多說(shuō)我們來(lái)直接看下它的實(shí)現(xiàn)邏輯

private async Task GetFormFilesAsync(string modelName,ModelBindingContext bindingContext,ICollection<IFormFile> postedFiles) {//獲取Request實(shí)例var request = bindingContext.HttpContext.Request;if (request.HasFormContentType){//獲取Request.Formvar form = await request.ReadFormAsync();//遍歷Request.Form.Filesforeach (var file in form.Files){//FileName如果未空的話不進(jìn)行模型綁定if (file.Length == 0 && string.IsNullOrEmpty(file.FileName)){continue;}//FileName等于模型綁定名稱(chēng)的話則添加postedFilesif (file.Name.Equals(modelName, StringComparison.OrdinalIgnoreCase)){postedFiles.Add(file);}}}else{_logger.CannotBindToFilesCollectionDueToUnsupportedContentType(bindingContext);} }

看到這里得到的思路就比較清晰了,由于源碼需要順著邏輯走,我們大致總結(jié)一下關(guān)于FormFile模型綁定相關(guān)

?為了統(tǒng)一處理方便,不管是上傳的是單個(gè)文件還是多個(gè)文件,都會(huì)被包裝成ICollection<IFormFile>集合類(lèi)型?ICollection<IFormFile>集合里的值就是來(lái)自于Request.Form.Files?可綁定的類(lèi)型IFormFile、List<IFormFile>、IFormFileCollection等都是由ICollection<IFormFile>里的數(shù)據(jù)初始化而來(lái)?如果模型參數(shù)類(lèi)型是IFormFile實(shí)例非集合類(lèi)型,那么會(huì)從ICollection<IFormFile>集合中獲取第一個(gè)?模型綁定的參數(shù)名稱(chēng)要和上傳的FileName保持一致,否則無(wú)法進(jìn)行模型綁定

RequestForm的Files來(lái)自何處

通過(guò)上面的模型綁定我們了解到了ICollection<IFormFile>的值來(lái)自Request.Form.Files而得到RequestForm的值是來(lái)自ReadFormAsync方法,那么我們就從這個(gè)方法入手看看RequestForm是如何被初始化的,這是一個(gè)擴(kuò)展方法來(lái)自于RequestFormReaderExtensions擴(kuò)展類(lèi)[點(diǎn)擊查看源碼????[5]]大致代碼如下

public static Task<IFormCollection> ReadFormAsync(this HttpRequest request, FormOptions options,CancellationToken cancellationToken = new CancellationToken()) {// 一堆判斷邏輯由此省略var features = request.HttpContext.Features;var formFeature = features.Get<IFormFeature>();//首次請(qǐng)求初始化沒(méi)有Form的時(shí)候初始化一個(gè)FormFeatureif (formFeature == null || formFeature.Form == null){features.Set<IFormFeature>(new FormFeature(request, options));}//調(diào)用了HttpRequest的ReadFormAsync方法return request.ReadFormAsync(cancellationToken); }

沒(méi)啥可說(shuō)的直接找到HttpRequest的ReadFormAsync方法,我們?cè)谏掀恼铝私膺^(guò)HttpRequest抽象類(lèi)默認(rèn)的實(shí)現(xiàn)類(lèi)是DefaultHttpRequest,所以我們找到DefaultHttpRequest的ReadFormAsync方法[點(diǎn)擊查看源碼????[6]]看一下它的實(shí)現(xiàn)

public override Task<IFormCollection> ReadFormAsync(CancellationToken cancellationToken) {return FormFeature.ReadFormAsync(cancellationToken); }

從代碼中可以看到ReadFormAsync方法的返回值值來(lái)自FormFeature的ReadFormAsync方法,找到FormFeature的定義

private IFormFeature FormFeature => _features.Fetch(ref _features.Cache.Form, this, _newFormFeature)!; //其中_newFormFeature的定義來(lái)自其中委托的r值就是DefaultHttpRequest實(shí)例 private readonly static Func<DefaultHttpRequest, IFormFeature> _newFormFeature = r => new FormFeature(r, r._context.FormOptions ?? FormOptions.Default);

通過(guò)上面這段兩段代碼我們可以看到,無(wú)論怎么兜兜轉(zhuǎn)轉(zhuǎn),最后都來(lái)到了FormFeature這個(gè)類(lèi),而且實(shí)例化這個(gè)類(lèi)的時(shí)候接受的值都是來(lái)自于DefaultHttpRequest實(shí)例,其中還包含F(xiàn)ormOptions,看著有點(diǎn)眼熟,不錯(cuò)上面我們?cè)O(shè)置的上傳大小限制值的屬性MultipartBodyLengthLimit正是來(lái)自這里。所有最終的單子都落到了FormFeature類(lèi)的ReadFormAsync方法[點(diǎn)擊查看源碼????[7]]找到源碼大致如下所示

public Task<IFormCollection> ReadFormAsync() => ReadFormAsync(CancellationToken.None); public Task<IFormCollection> ReadFormAsync(CancellationToken cancellationToken) {if (_parsedFormTask == null){if (Form != null){_parsedFormTask = Task.FromResult(Form);}else{_parsedFormTask = InnerReadFormAsync(cancellationToken);}}return _parsedFormTask; }

最終指向了InnerReadFormAsync這個(gè)方法,而這個(gè)方法正是初始化Form的所在,也就是說(shuō)涉及到Form的初始化相關(guān)操作就是在這里進(jìn)行的,因?yàn)檫@個(gè)方法的邏輯比較多所以我們只關(guān)注ContentType是multipart/form-data的邏輯,這里我們也就只保留這類(lèi)的相關(guān)邏輯省去了其他的邏輯,有需要了解的同學(xué)可以自行查看源碼[點(diǎn)擊查看源碼????[8]]

private async Task<IFormCollection> InnerReadFormAsync(CancellationToken cancellationToken) {FormFileCollection? files = null;using (cancellationToken.Register((state) => ((HttpContext)state!).Abort(), _request.HttpContext)){var contentType = ContentType;// 判斷ContentType為multipart/form-data的時(shí)候if (HasMultipartFormContentType(contentType)){var formAccumulator = new KeyValueAccumulator();//得到boundary數(shù)據(jù)//Content-Type: multipart/form-data; boundary="----WebKitFormBoundarymx2fSWqWSd0OxQqq"var boundary = GetBoundary(contentType, _options.MultipartBoundaryLengthLimit);// 把針對(duì)文件上傳的部分封裝到MultipartReadervar multipartReader = new MultipartReader(boundary, _request.Body){//Header個(gè)數(shù)限制HeadersCountLimit = _options.MultipartHeadersCountLimit,//Header長(zhǎng)度限制HeadersLengthLimit = _options.MultipartHeadersLengthLimit,//Body長(zhǎng)度限制BodyLengthLimit = _options.MultipartBodyLengthLimit,};//獲取下一個(gè)可解析的節(jié)點(diǎn),可以理解為每一個(gè)要解析的上傳文件信息var p = await multipartReader.ReadNextSectionAsync(cancellationToken);//不為null說(shuō)明已從Body解析出的上傳文件信息while (p != null){// 在這里解析內(nèi)容配置并進(jìn)一步傳遞它以避免重新分析if (!ContentDispositionHeaderValue.TryParse(p.ContentDisposition, out var contentDisposition)){throw new InvalidDataException("");}if (contentDisposition.IsFileDisposition()){var fileSection = new FileMultipartSection(p, contentDisposition);// 如果尚未對(duì)整個(gè)正文執(zhí)行緩沖,則為文件啟用緩沖p.EnableRewind(_request.HttpContext.Response.RegisterForDispose,_options.MemoryBufferThreshold, _options.MultipartBodyLengthLimit);// 找到結(jié)尾await p.Body.DrainAsync(cancellationToken);var name = fileSection.Name;var fileName = fileSection.FileName;FormFile file;//判斷Body默認(rèn)的流是否被修改過(guò),比如開(kāi)啟緩沖就會(huì)修改//如果Body不是默認(rèn)流則直接服務(wù)Bodyif (p.BaseStreamOffset.HasValue){file = new FormFile(_request.Body, p.BaseStreamOffset.GetValueOrDefault(), p.Body.Length, name, fileName);}else{// 如果沒(méi)有被修改過(guò)則獲取MultipartReaderStream的實(shí)例file = new FormFile(p.Body, 0, p.Body.Length, name, fileName);}file.Headers = new HeaderDictionary(p.Headers);//如果解析出來(lái)了文件信息則初始化FormFileCollectionif (files == null){files = new FormFileCollection();}if (files.Count >= _options.ValueCountLimit){throw new InvalidDataException("");}files.Add(file);}else if (contentDisposition.IsFormDisposition()){var formDataSection = new FormMultipartSection(p, contentDisposition);var key = formDataSection.Name;var value = await formDataSection.GetValueAsync();formAccumulator.Append(key, value);if (formAccumulator.ValueCount > _options.ValueCountLimit){throw new InvalidDataException("");}}else{//沒(méi)解析出來(lái)類(lèi)型}p = await multipartReader.ReadNextSectionAsync(cancellationToken);}if (formAccumulator.HasValues){formFields = new FormCollection(formAccumulator.GetResults(), files);}}}// 如果可重置,則恢復(fù)讀取位置為0(因?yàn)锽ody被讀取到了尾部)if (_request.Body.CanSeek){_request.Body.Seek(0, SeekOrigin.Begin);}//通過(guò)files得到FormCollectionif (files != null){Form = new FormCollection(null, files);}return Form; }

這部分源碼比較多,而且這還是精簡(jiǎn)過(guò)只剩下ContentType為multipart/form-data的內(nèi)容,不過(guò)從這里我們就可以看出來(lái)FormFile的實(shí)例確實(shí)是依靠Request的Body里。其核心就在MultipartReader類(lèi)的ReadNextSectionAsync方法返回的Section數(shù)據(jù)[點(diǎn)擊查看源碼????[9]]通過(guò)上面的循環(huán)可以看到它是循環(huán)讀取的,它通過(guò)解析Request信息持續(xù)的迭代MultipartSection信息,這種操作方式正是處理一次上傳存在多個(gè)文件的情況,具體操作如下所示

private readonly BufferedReadStream _stream; private readonly MultipartBoundary _boundary; private MultipartReaderStream _currentStream;public MultipartReader(string boundary, Stream stream, int bufferSize) {//stream即是傳遞下來(lái)的RequestBody_stream = new BufferedReadStream(stream, bufferSize);_boundary = new MultipartBoundary(boundary, false);//創(chuàng)建MultipartReaderStream實(shí)例_currentStream = new MultipartReaderStream(_stream, _boundary) { LengthLimit = HeadersLengthLimit }; }public async Task<MultipartSection?> ReadNextSectionAsync(CancellationToken cancellationToken = new CancellationToken()) {//清空上一個(gè)節(jié)點(diǎn)的信息await _currentStream.DrainAsync(cancellationToken);// 如果返回了空值表示為最后一個(gè)節(jié)點(diǎn)if (_currentStream.FinalBoundaryFound){// 清空最后一個(gè)節(jié)點(diǎn)的掛載數(shù)據(jù)await _stream.DrainAsync(HeadersLengthLimit, cancellationToken);return null;}//讀取header信息var headers = await ReadHeadersAsync(cancellationToken);_boundary.ExpectLeadingCrlf = true;//組裝MultipartReaderStream實(shí)例_currentStream = new MultipartReaderStream(_stream, _boundary) { LengthLimit = BodyLengthLimit };//判斷流是否是原始的HttpRequestStreamlong? baseStreamOffset = _stream.CanSeek ? (long?)_stream.Position : null;//通過(guò)上面信息構(gòu)造MultipartSection實(shí)例return new MultipartSection() { Headers = headers, Body = _currentStream, BaseStreamOffset = baseStreamOffset }; }

這里可以看出傳遞下來(lái)的RequestBody被構(gòu)建出了MultipartReaderStream實(shí)例,即MultipartReaderStream包裝了RequestBody中的信息[點(diǎn)擊查看源碼????[10]]看名字也知道它也是實(shí)現(xiàn)了Stream抽象類(lèi)

internal sealed class MultipartReaderStream : Stream { }

而且我們看到BodyLengthLimit正是傳遞給了它的LengthLimit屬性,而B(niǎo)odyLengthLimit正是設(shè)置限制上傳文件的大小的屬性,我們找到使用LengthLimit屬性的地方,代碼如下所示[點(diǎn)擊查看源碼????[11]]

private int UpdatePosition(int read) {//更新Stream的Position的值,即更新讀取位置_position += read;//繼續(xù)讀取if (_observedLength < _position){//保存已經(jīng)讀取了的位置_observedLength = _position;//如果讀取了位置大于LengthLimit則拋出異常if (LengthLimit.HasValue && _observedLength > LengthLimit.GetValueOrDefault()){throw new InvalidDataException($"Multipart body length limit {LengthLimit.GetValueOrDefault()} exceeded.");}}return read; }

從這段代碼我們可以看出,正是此方法限制了讀取的Body大小,通過(guò)我們對(duì)Stream的了解,這個(gè)UpdatePosition方法也必然會(huì)在Stream的Read方法也即是此處的MultipartReaderStream的Read方法中調(diào)用[點(diǎn)擊查看源碼????[12]]這樣才能起到限制的作用,大致看一下Read方法的實(shí)現(xiàn)代碼

public override int Read(byte[] buffer, int offset, int count) {//如果已經(jīng)讀到了結(jié)尾則直接返回0if (_finished){return 0;}PositionInnerStream();var bufferedData = _innerStream.BufferedData;// 匹配boundary的讀取邊界int read;if (SubMatch(bufferedData, _boundary.BoundaryBytes, out var matchOffset, out var matchCount)){// 匹配到了可讀取的邊界讀取并返回if (matchOffset > bufferedData.Offset){read = _innerStream.Read(buffer, offset, Math.Min(count, matchOffset - bufferedData.Offset));//返回讀取的長(zhǎng)度正是調(diào)用的UpdatePositionreturn UpdatePosition(read);}var length = _boundary.BoundaryBytes.Length;Debug.Assert(matchCount == length);var boundary = _bytePool.Rent(length);read = _innerStream.Read(boundary, 0, length);_bytePool.Return(boundary);Debug.Assert(read == length);//讀取RequestBody信息var remainder = _innerStream.ReadLine(lengthLimit: 100);remainder = remainder.Trim();//說(shuō)明讀取到了boundary的結(jié)尾if (string.Equals("--", remainder, StringComparison.Ordinal)){FinalBoundaryFound = true;}Debug.Assert(FinalBoundaryFound || string.Equals(string.Empty, remainder, StringComparison.Ordinal), "Un-expected data found on the boundary line: " + remainder);_finished = true;//返回讀取的長(zhǎng)度0說(shuō)明讀到了結(jié)尾return 0;}read = _innerStream.Read(buffer, offset, Math.Min(count, bufferedData.Count));//這里同樣是UpdatePositionreturn UpdatePosition(read); }

通過(guò)這里就可清楚的看到MultipartReaderStream的Read方法就是在解析讀取的RequestBody的FormData類(lèi)型的信息,解析成我們可以直接讀取或者直接保存成文件的的原始的文件信息,它還有一個(gè)異步讀取的ReadAsync方法其實(shí)現(xiàn)原理類(lèi)似,在這里咱們就不在展示源碼了。最后我們?cè)賮?lái)看一下MultipartSection類(lèi)的實(shí)現(xiàn)[點(diǎn)擊查看源碼????[13]]我們上面知道了MultipartReaderStream才是在RequestBody中解析到文件上傳信息的關(guān)鍵所在,因此MultipartSection也就是包裝了讀取好的文件信息,我們來(lái)看一下它的代碼實(shí)現(xiàn)

public class MultipartSection {/// <summary>/// 從header中得到的ContentType類(lèi)型/// </summary>public string? ContentType{get{if (Headers != null && Headers.TryGetValue(HeaderNames.ContentType, out var values)){return values;}return null;}}/// <summary>/// 從header中得到的ContentDisposition信息/// </summary>public string? ContentDisposition{get{if (Headers != null && Headers.TryGetValue(HeaderNames.ContentDisposition, out var values)){return values;}return null;}}/// <summary>/// 讀取到的Header信息/// </summary>public Dictionary<string, StringValues>? Headers { get; set; }/// <summary>/// 從RequestBody中解析到的Stream信息,即MultipartReaderStream或其他RequestBody實(shí)例/// </summary>public Stream Body { get; set; } = default!;/// <summary>/// 已經(jīng)被讀取過(guò)的Stream位置/// </summary>public long? BaseStreamOffset { get; set; } }

不出所料,這個(gè)類(lèi)正是包裝了上面一堆針對(duì)HTTP請(qǐng)求信息中讀取到的關(guān)于上傳的文件信息,由于上面設(shè)計(jì)到了幾個(gè)類(lèi),而且設(shè)計(jì)到了一個(gè)大致的讀取流程,為了防止同學(xué)們看起來(lái)容易蒙圈,這里咱們大致總結(jié)一下這里的讀取流程。通過(guò)上面的代碼我們了解到了涉及到的幾個(gè)重要的類(lèi)MultipartReader、MultipartReaderStream、MultipartSection知道這幾個(gè)類(lèi)在做什么就能明白到底是怎么通過(guò)RequestBody解析到文件信息的。大致解釋一下這幾個(gè)類(lèi)在做些什么

?通過(guò)MultipartReader類(lèi)的ReadNextSectionAsync方法可以得到MultipartSection的實(shí)例?MultipartSection類(lèi)包含的就是解析出RequestBody里的文件相關(guān)的信息包裝起來(lái),MultipartSection的Body屬性的值正是MultipartReaderStream的實(shí)例。?MultipartReaderStream類(lèi)正是通過(guò)讀取RequestBody里的各種boundary信息轉(zhuǎn)換為原始的文件內(nèi)容的Stream信息?FormFile的CopyToAsync和OpenReadStream方法都是Stream操作,而操作的Stream是來(lái)自MultipartReaderStream實(shí)例

總結(jié)

????這次的分析差不多就到這里了, 本篇文章主要討論了ASP.NET Core文件上傳操作類(lèi)IFormFile與RequestBody的關(guān)系,即如果通過(guò)RequestBody得到IFormFile實(shí)例相關(guān),畢竟是源碼設(shè)計(jì)到的東西比較多也比較散亂,我們?cè)賮?lái)大致的總結(jié)一下

?無(wú)論在Action上對(duì)IFormFile、List<IFormFile>、IFormFileCollection等進(jìn)行模型綁定,其實(shí)都是來(lái)自模型綁定處理類(lèi)FormFileModelBinder,而這個(gè)類(lèi)正是根據(jù)Request.Form.File的處理來(lái)判斷如何進(jìn)行模型綁定的。?而Request.Form.File本身其實(shí)就是IFormFileCollection類(lèi)型的,它的值也正是來(lái)自對(duì)RequestBody的解析,也正是我們今天的結(jié)論File的值來(lái)自RequestBody。?從RequestBody解析到IFormFileCollection是一個(gè)過(guò)程,而IFormFileCollection實(shí)際上是IFormFile的集合類(lèi)型,從RequestBody解析出來(lái)的也是單個(gè)IFormFile類(lèi)型,通過(guò)不斷的迭代添加得到的IFormFileCollection集合。?而從RequestBody中解析出來(lái)上傳的文件到IFormFile涉及到了幾個(gè)核心類(lèi),即MultipartReader、MultipartReaderStream和MultipartSection。其中MultipartSection是通過(guò)MultipartReader的ReadNextSectionAsync方法得到的,里面包含了解析好的上傳文件相關(guān)信息。而MultipartSection正是包裝了MultipartReaderStream,而這個(gè)類(lèi)才是真正讀取RequestBody得到可讀取的文件原始Stream的關(guān)鍵所在。

到了這里本文的全部?jī)?nèi)容就差不多結(jié)束了,希望本文能給大家?guī)?lái)收獲。我覺(jué)得有時(shí)候看源碼能解決許多問(wèn)題和心中的疑惑,因?yàn)槲覀冏鳛槌绦騿T每天寫(xiě)的也就是代碼,所以沒(méi)有比程序員直接讀取代碼能更好的了解想了解的信息了。但是讀源碼也有一定的困難,畢竟是別人的代碼,思維存在一定的偏差,更何況是一些優(yōu)秀的框架,作者們的思維很可能比我們要高出很多,所以很多時(shí)候讀起來(lái)會(huì)非常的吃力,即便如此筆者也覺(jué)得讀源碼是了解框架得到框架信息的一種比較行之有效的方式。

References

[1]?深入探究ASP.NET Core讀取Request.Body的正確方式:?https://www.cnblogs.com/wucy/p/14699717.html
[2]?點(diǎn)擊查看源碼????:?https://github.com/dotnet/aspnetcore/blob/v5.0.6/src/Mvc/Mvc.Core/src/ModelBinding/Binders/FormFileModelBinder.cs#L38
[3]?點(diǎn)擊查看源碼????:?https://github.com/dotnet/aspnetcore/blob/v5.0.6/src/Mvc/Mvc.Core/src/ModelBinding/ModelBindingHelper.cs
[4]?點(diǎn)擊查看源碼????:?https://github.com/dotnet/aspnetcore/blob/v5.0.6/src/Mvc/Mvc.Core/src/ModelBinding/Binders/FormFileModelBinder.cs#L142
[5]?點(diǎn)擊查看源碼????:?https://github.com/dotnet/aspnetcore/blob/v5.0.6/src/Http/Http/src/RequestFormReaderExtensions.cs#L21
[6]?點(diǎn)擊查看源碼????:?https://github.com/dotnet/aspnetcore/blob/v5.0.6/src/Http/Http/src/Internal/DefaultHttpRequest.cs#L166
[7]?點(diǎn)擊查看源碼????:?https://github.com/dotnet/aspnetcore/blob/v5.0.6/src/Http/Http/src/Features/FormFeature.cs#L108
[8]?點(diǎn)擊查看源碼????:?https://github.com/dotnet/aspnetcore/blob/v5.0.6/src/Http/Http/src/Features/FormFeature.cs#L125
[9]?點(diǎn)擊查看源碼????:?https://github.com/dotnet/aspnetcore/blob/v5.0.6/src/Http/WebUtilities/src/MultipartReader.cs#L68:46
[10]?點(diǎn)擊查看源碼????:?https://github.com/dotnet/aspnetcore/blob/v5.0.6/src/Http/WebUtilities/src/MultipartReaderStream.cs
[11]?點(diǎn)擊查看源碼????:?https://github.com/dotnet/aspnetcore/blob/v5.0.6/src/Http/WebUtilities/src/MultipartReaderStream.cs#L148
[12]?點(diǎn)擊查看源碼????:?https://github.com/dotnet/aspnetcore/blob/v5.0.6/src/Http/WebUtilities/src/MultipartReaderStream.cs#L162
[13]?點(diǎn)擊查看源碼????:?https://github.com/dotnet/aspnetcore/blob/v5.0.6/src/Http/WebUtilities/src/MultipartSection.cs

總結(jié)

以上是生活随笔為你收集整理的ASP.NET Core文件上传IFormFile于Request.Body的羁绊的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

91精品在线视频观看 | 在线观看视频h | 国产韩国日本高清视频 | 国产黄色高清 | 亚洲精品在线二区 | 久久精品欧美日韩精品 | 婷婷干五月 | 日韩精品免费一区二区 | 欧美先锋影音 | 日韩欧美在线一区二区 | 97精产国品一二三产区在线 | 日韩国产精品一区 | 婷婷国产精品 | 美女视频是黄的免费观看 | 亚洲毛片视频 | 精品主播网红福利资源观看 | 久久免费视频在线观看 | 精品国产一区二区三区四区在线观看 | 91精品成人 | 色多多污污| 精品人人人 | 日本公妇色中文字幕 | 亚洲一级片免费观看 | 香蕉视频免费在线播放 | 精品亚洲免费视频 | 成人免费观看视频网站 | 国产a级精品 | 亚洲午夜精品一区二区三区电影院 | 免费看污黄网站 | 亚洲综合婷婷 | 狠狠干夜夜操天天爽 | 成人免费在线观看电影 | 国产+日韩欧美 | 久久久久久草 | 欧美日韩一区二区久久 | 国产小视频免费在线网址 | 四虎www com| 久久综合激情 | 成人av电影在线观看 | 欧美激情综合五月色丁香小说 | 国产精品美女在线 | 91在线看视频免费 | 国产视频一区二区三区在线 | 在线看片成人 | 国产在线色视频 | 国产无套精品久久久久久 | 欧美激情精品久久久久久 | 国产精品免费av | 人人看97 | 国产精品免费大片视频 | 日韩在线无 | 久久视频这里有久久精品视频11 | 精品久久久久国产 | 97碰视频| 日韩久久久久久久久久 | 最新亚洲视频 | 亚洲日本精品 | 97视频免费观看 | 日韩精品免费在线 | 欧美国产日韩一区二区三区 | 国产三级午夜理伦三级 | 久久久久久久久久久成人 | 精精国产xxxx视频在线播放 | 日韩欧美精品在线观看 | 特级西西444www大胆高清无视频 | 久久久久国产精品免费免费搜索 | 国产精品久久久视频 | 亚洲国产免费看 | 久久久一本精品99久久精品 | 成人在线播放免费观看 | 午夜视频在线瓜伦 | 国产精品va在线观看入 | 热久久国产精品 | 97在线观看 | 黄色三级免费观看 | 国产精品视频专区 | 超级碰碰碰碰 | 99欧美视频 | 国产高清视频免费观看 | 久久五月婷婷丁香社区 | 婷婷在线免费视频 | 香蕉视频在线网站 | 国产不卡在线观看 | 黄色网在线播放 | 国产美女精品视频免费观看 | 国产欧美精品一区二区三区 | 激情网站五月天 | 国产在线观看h | 婷婷在线色 | 久久综合久久综合九色 | 国产欧美综合视频 | 成人sm另类专区 | 天天人人 | 亚洲高清在线视频 | 国产精品入口传媒 | 国产黄影院色大全免费 | 亚洲精品视频中文字幕 | 97精品超碰一区二区三区 | 黄色特一级 | 96视频免费在线观看 | 欧美日韩国产二区三区 | 国产在线精品二区 | 久久爱资源网 | 国产精品白丝jk白祙 | 免费观看特级毛片 | 国产精品久久久久久五月尺 | 日本最新一区二区三区 | 中文字幕你懂的 | www.五月激情.com| 人人狠狠综合久久亚洲婷 | 国产九九九精品视频 | 日韩高清 一区 | 日韩在线一二三区 | av黄色免费在线观看 | 天堂在线一区 | 成人av电影免费在线播放 | 欧美日韩视频一区二区三区 | 久久久久网站 | 伊人色**天天综合婷婷 | 九九免费在线看完整版 | 国产中文字幕av | 一级黄视频 | 超碰在线cao | 欧美色图30p| 99久久精品免费看国产 | 亚洲区精品视频 | 久久国产女人 | 天天射色综合 | 黄色免费网站下载 | 正在播放国产一区 | 亚洲日韩精品欧美一区二区 | 国内精品久久久久影院日本资源 | 日韩精品视频免费在线观看 | 免费av高清 | v片在线播放| 国产福利一区二区在线 | 美女久久久久久久久久 | 一区二区三区污 | 日韩视频精品在线 | 91精品国产99久久久久久红楼 | 国产在线观看你懂的 | 狠狠色香婷婷久久亚洲精品 | 首页av在线 | 久久久久久激情 | 偷拍福利视频一区二区三区 | av成人在线播放 | 182午夜在线观看 | 天天鲁一鲁摸一摸爽一爽 | 日韩一级黄色av | 中文字幕在线影院 | 国产福利一区二区三区在线观看 | 中文日韩在线视频 | 日韩字幕| 久久久精品综合 | av国产在线观看 | 国产精品久久久久久69 | 丁香婷婷激情网 | 免费看一级黄色 | 日韩免费高清在线 | 精品在线观看国产 | 黄色在线观看免费网站 | 成人久久18免费网站 | 麻豆视频大全 | 欧美精品九九99久久 | 日韩伦理片一区二区三区 | 国内外成人在线视频 | 久草在线资源观看 | 国产精品久久久久久久久久久免费 | 91久久国产综合精品女同国语 | 成人国产精品 | 久久综合久久综合九色 | 亚洲欧洲国产精品 | 亚洲天堂网视频 | 日av免费 | 午夜av片 | 一区二区三区日韩在线观看 | av在线小说| 中文字幕一区二区在线播放 | 性色大片在线观看 | 亚洲 综合 专区 | 一本色道久久综合亚洲二区三区 | 五月天激情电影 | 99久精品视频 | 亚洲人成人99网站 | av福利超碰网站 | 美女网站视频免费黄 | 欧美日韩免费一区二区 | 欧美专区国产专区 | 久久免费精品国产 | 国产精品理论片在线播放 | 免费69视频 | 亚洲综合五月 | 99精品欧美一区二区 | 久久国产精品久久精品 | 中文资源在线播放 | 精品国产精品久久一区免费式 | 911精品视频 | 国产很黄很色的视频 | 久久九九久久 | 久久九九久久 | 国产视频1区2区 | aav在线| 99九九99九九九视频精品 | 深夜国产在线 | 国产一区二区在线精品 | 国产又粗又猛又黄视频 | 欧美日韩国产一区二区三区在线观看 | 国产一级二级视频 | 夜夜躁日日躁狠狠躁 | 国产一区二区三区黄 | 久久精品日产第一区二区三区乱码 | 久章操 | 国产视频一区在线 | 日本 在线 视频 中文 有码 | 亚洲成人精品在线观看 | 少妇精69xxtheporn | 天天色综合天天 | 国产精品一区专区欧美日韩 | 97超碰人人澡人人爱学生 | 99久久精品久久久久久清纯 | 极品嫩模被强到高潮呻吟91 | 久久优 | 国产很黄很色的视频 | 狠狠干狠狠久久 | 精品久久91 | 97在线观看视频国产 | 蜜桃视频在线视频 | 91完整版| 日韩精品aaa | 亚洲资源片 | 久久色亚洲 | 国产免费黄色 | 国产不卡视频 | 黄色日批网站 | 999精品网| 黄色网www| 天天干天天干天天干天天干天天干天天干 | 黄色免费网战 | 国产成人精品午夜在线播放 | 久久99久久99 | av免费电影网站 | 超碰在线9 | 深爱婷婷激情 | 99r在线观看 | 日韩一区二区三区高清免费看看 | 色成人亚洲网 | 亚洲视频第一页 | 一区二区三区四区在线 | 国产精品毛片久久久久久久 | 天天色影院| 中文字幕 二区 | 久精品视频 | 日日干夜夜骑 | 在线天堂中文在线资源网 | 国产精品国产三级国产aⅴ入口 | 久久视频在线视频 | 狠狠色丁香婷婷综合 | 手机在线日韩视频 | 成人免费 在线播放 | 国产精品久久伊人 | 精品美女在线视频 | 国产精品手机视频 | 欧美 激情 国产 91 在线 | 久草视频免费在线观看 | 久久久精品国产一区二区电影四季 | 免费看的黄色 | 久久久久久高清 | a一片一级 | www.操.com| 午夜精品剧场 | 久久久精品电影 | 亚洲全部视频 | 亚洲午夜精品福利 | 我要色综合天天 | 日本动漫做毛片一区二区 | 欧美做受xxx| 最新免费中文字幕 | 久久色在线观看 | 少妇bbb搡bbbb搡bbbb | 韩国av一区 | 在线看91| 91香蕉视频在线下载 | 日韩精品视| 亚洲精品www久久久久久 | 日本黄色片一区二区 | 91亚洲精品久久久蜜桃借种 | 国产免费观看久久 | 成年人免费看片网站 | 欧美色婷婷| 色吊丝在线永久观看最新版本 | 日韩理论视频 | 人人爱在线视频 | 六月丁香婷 | 99国产一区 | 91桃色国产在线播放 | 色婷婷电影网 | www.伊人色.com | 亚洲天天摸日日摸天天欢 | 国产原厂视频在线观看 | 日日精品 | 久久久www成人免费精品 | 国产资源在线免费观看 | 精品人人人 | 夜夜爽天天爽 | 久久精品超碰 | 91av免费观看| 麻豆成人精品 | 中文字幕在线观看2018 | 日韩在线网址 | 日韩在观看线 | 欧美一级xxxx| 亚洲欧美少妇 | 久久久久视 | 碰超在线97人人 | 欧洲精品视频一区 | 懂色av一区二区在线播放 | 久一久久 | 成人蜜桃 | 欧美天天射 | 国产一区在线播放 | 免费黄色激情视频 | 亚洲免费高清视频 | 亚洲午夜av久久乱码 | 国产无套一区二区三区久久 | 88av视频| 九九免费视频 | 国产亚洲久一区二区 | 成人a视频在线观看 | 国产一区在线播放 | 亚洲女人天堂成人av在线 | 久久精品视频免费 | 国产原厂视频在线观看 | 日韩精品在线视频 | 91禁看片| 国产高清视频在线 | 国产视频久久久久 | 国产91小视频 | 九九热视频在线播放 | 国产97在线视频 | 国产99久久久精品 | 精品色综合 | 日韩在线观看精品 | 欧美,日韩 | 亚洲一区久久久 | 国产精品免费一区二区三区在线观看 | 精品欧美一区二区三区久久久 | 午夜婷婷在线观看 | 久久九九影视网 | 激情欧美一区二区免费视频 | 成年人在线免费看片 | 超碰97国产在线 | 又污又黄网站 | 免费人成在线观看 | 欧美精品日韩 | 精品黄色片 | 精品视频免费 | 日韩免费中文字幕 | 天天射天 | 午夜黄色影院 | 青草视频免费观看 | 国产综合小视频 | 一区二区三区精品在线 | 欧美激情视频在线观看免费 | 在线观看精品视频 | 69xxxx欧美 | 91麻豆传媒 | 天天干,天天操 | 怡红院av久久久久久久 | 综合中文字幕 | 九九九九九精品 | 国产黄色理论片 | 久久精品欧美日韩精品 | 五月激情站 | 亚洲激情六月 | 国产高清久久 | 日韩理论片在线 | 亚洲激情视频在线观看 | 91网页版免费观看 | 狠狠狠狠狠干 | 91最新国产| 国产精品精品视频 | 免费在线观看一区二区三区 | 欧美九九视频 | 狠狠狠狠狠狠狠狠 | 九九精品久久久 | 91伊人影院| 91精品在线观看入口 | 91福利视频久久久久 | 国产精品手机在线 | 91理论片午午伦夜理片久久 | 久久免费大片 | 欧美另类美少妇69xxxx | 国产精品欧美精品 | 成人免费在线播放 | 高清不卡一区二区在线 | 亚洲天天摸日日摸天天欢 | 国产中文视 | 精品国产区 | 免费黄色激情视频 | 日本午夜在线亚洲.国产 | 波多野结衣小视频 | 欧美精品久久久久久久久久丰满 | 欧美日韩中文字幕综合视频 | 国产伦精品一区二区三区免费 | 中文字幕一区二区三区四区视频 | 国产精品99免视看9 国产精品毛片一区视频 | 夜夜躁日日躁 | 日本性久久 | 午夜影院一级片 | 欧美激情精品久久久久久免费印度 | 激情久久影院 | 久久在线看 | 亚洲综合精品视频 | 999国产精品视频 | 麻豆91在线 | 天天se天天cao天天干 | 最新成人在线 | 超碰在线97国产 | 日韩1页 | 国产又黄又爽又猛视频日本 | 欧美成人在线网站 | 亚洲精品777 | 在线观看成人小视频 | 狠狠五月天 | 夜色成人av | 久久99视频精品 | 久久久久女人精品毛片 | 天天草天天草 | 日本69hd | 伊人欧美| 国产黄色一级片在线 | 亚洲精品色视频 | 18国产精品白浆在线观看免费 | 一区中文字幕 | 日韩网站在线看片你懂的 | 色综合人人 | 亚洲天堂精品 | 97电影在线观看 | 欧美日韩在线看 | 国产精品手机看片 | 99视频精品视频高清免费 | 五月婷网站 | 国产精品入口传媒 | 亚洲爱视频 | 婷婷色综合网 | 久精品视频在线观看 | 涩涩资源网 | 丁香影院在线 | 91pony九色丨交换 | 久久视了| 日韩免费视频观看 | 91成人在线免费观看 | 91九色在线观看视频 | 久久精品人人做人人综合老师 | 欧美另类高潮 | 四虎影视久久久 | 在线视频观看你懂的 | 欧美日韩色婷婷 | 天堂激情网| 干av在线 | 久久视频精品 | 激情五月激情综合网 | 国产精品18毛片一区二区 | 91看片淫黄大片在线播放 | 99精品视频播放 | 日韩aa视频 | 91九色自拍| 特级黄色视频毛片 | 国产一区二区三区网站 | 免费国产视频 | 欧美日本一二三 | 国产精品欧美在线 | av不卡免费看 | 公与妇乱理三级xxx 在线观看视频在线观看 | 99在线免费观看视频 | 岛国精品一区二区 | 国产黄色精品在线 | 日韩精品资源 | 欧美成人在线免费 | 国产尤物在线视频 | 亚洲特级片 | 狠狠干狠狠插 | 午夜三级影院 | 久久综合中文字幕 | 精品久久久免费 | 久久久久久久国产精品视频 | 久久久精品视频网站 | 久久久久久久久久久高潮一区二区 | 超碰在线日韩 | 日本精品视频免费 | 91精彩视频在线观看 | 黄色精品一区 | 成人亚洲综合 | 精品视频免费观看 | 9999免费视频 | 久久亚洲综合国产精品99麻豆的功能介绍 | 91麻豆精品国产91久久久久久 | 91av看片 | 国产91精品看黄网站 | 五月婷婷视频在线 | 免费看久久 | 国产亚洲精品女人久久久久久 | 亚洲日本色 | 操操色 | 中文字幕一区二区三区四区在线视频 | 激情综合亚洲 | 亚洲国产高清在线观看视频 | 国产在线探花 | 国产精品久久久久久久av大片 | 亚洲无人区小视频 | 欧美成人一区二区 | 91精品久久久久久综合乱菊 | 欧美日韩二三区 | 又黄又爽的免费高潮视频 | 日韩一区二区三区在线观看 | 亚洲日b视频 | 黄色大全免费观看 | 成年人三级网站 | 亚洲婷婷丁香 | 欧美日韩国产一区二区三区 | 国产手机av在线 | 人人插人人艹 | 国产激情小视频在线观看 | 欧美精品在线观看一区 | 欧美激情精品久久久久久变态 | 国产精品黑丝在线观看 | 欧美视频二区 | 午夜精品一区二区三区在线 | 国产高清视频 | 久久系列| 最近中文字幕视频完整版 | 亚洲男男gⅴgay双龙 | 久久精品视频在线观看免费 | 国产在线看一区 | 久草9视频| 久久久久久久久久久综合 | 日韩精品综合在线 | 午夜精品久久久久久久99婷婷 | 国产 一区二区三区 在线 | 97香蕉久久超级碰碰高清版 | av一级片| 伊人色综合网 | 狠狠躁夜夜躁人人爽超碰97香蕉 | 黄色毛片一级片 | 欧美黄在线 | 精品一区二区三区久久久 | 亚洲免费资源 | 91免费黄视频 | 草久在线观看视频 | 国产精品99久久久精品 | 精品中文字幕视频 | 国产三级午夜理伦三级 | 九九在线免费视频 | 三日本三级少妇三级99 | 国产成人三级三级三级97 | 伊人激情网 | 99精品欧美一区二区三区 | 最近免费中文字幕mv在线视频3 | 婷婷丁香久久五月婷婷 | 欧美日韩国产色综合一二三四 | 亚洲欧美成人 | 国产成人在线观看免费 | 日韩精品一区二区三区丰满 | 在线免费观看一区二区三区 | 日韩免费中文字幕 | 欧美片一区二区三区 | 日韩精选在线 | 国产又黄又爽又猛视频日本 | 天天色天天操天天爽 | 日韩精品视频久久 | 欧美激情视频一区二区三区免费 | 国产日韩亚洲 | 中文字幕一区二区三 | 精品免费在线视频 | 亚洲综合色av | www.一区二区三区 | 国产成人久久精品77777 | 国产精品视频 | 五月天天在线 | 国产91成人在在线播放 | 91精选在线| 久久久wwww | 麻豆免费观看视频 | 久久尤物电影视频在线观看 | 国产一级不卡视频 | 97爱 | 国产精品久久久久久爽爽爽 | www免费 | 涩涩网站在线播放 | 国产精品亚洲综合久久 | 成人aaa毛片 | 国产一区二区影院 | 国产亚洲精品久久久久久网站 | 国产成人三级在线播放 | 中文字幕在线观看2018 | 黄色电影网站在线观看 | 精品视频资源站 | 精品一区精品二区 | 日韩aa视频 | 久久免费视频国产 | 成人精品一区二区三区电影免费 | 超碰成人网 | 69视频国产 | 国产美女精彩久久 | 人人爽人人爽人人爽人人爽 | 日韩电影中文字幕 | 亚洲女同ⅹxx女同tv | 免费a网| 69久久久 | 在线观看视频97 | 免费看片色| 久久一区二区三区国产精品 | 亚洲电影自拍 | 色资源网在线观看 | a天堂一码二码专区 | 97超碰国产精品 | 日韩在线二区 | 国内精品久久天天躁人人爽 | av电影中文 | 国产亚洲无 | 久免费| 狠狠的干狠狠的操 | 色综合天天视频在线观看 | 欧美性脚交 | 国产不卡视频在线播放 | 91在线播 | 精品国产一区二区三区久久久蜜臀 | 少妇bbbb| 香蕉日日 | 欧美一区视频 | 最新av网站在线观看 | 国产精品毛片一区视频播 | 亚洲黄色影院 | 日韩精品一区二区三区高清免费 | 免费久久网站 | 中文字幕观看视频 | 国产精品中文在线 | 精品国产成人 | 日日夜夜精品免费观看 | 日本精品久久久久中文字幕 | 最新av免费 | 中文永久免费观看 | 亚洲国产97在线精品一区 | 亚洲精品白浆高清久久久久久 | 精品伦理一区二区三区 | 人人澡人人干 | 久久精品导航 | 亚洲精品五月 | 69国产盗摄一区二区三区五区 | 精品国产一区在线观看 | 国产一区私人高清影院 | 99视频播放| 亚洲国产日韩av | 亚洲国产理论片 | 九草在线视频 | 久久精品一区二区三区视频 | 精品不卡av | 伊人热 | 国产精品色 | 国产精品成人免费 | 精品国产aⅴ一区二区三区 在线直播av | 少妇自拍av | 国产精品资源在线观看 | 亚洲综合成人在线 | 免费成人黄色av | 久久免费国产精品1 | 久草在线一免费新视频 | 日本黄色免费在线观看 | 亚洲精品视频网址 | 欧美日韩视频在线一区 | 国产爽视频 | 91福利在线观看 | 亚洲精品成人在线 | 伊人天天综合 | 欧美久久久久 | 91在线观看视频网站 | 美腿丝袜av | 色av资源网 | 久久在线视频在线 | 精品国偷自产在线 | 日日色综合 | 激情五月播播久久久精品 | 日本在线视频网址 | 九九视频精品免费 | 91视频 - x99av| 国产高清av免费在线观看 | 欧美日韩免费一区二区三区 | 日本一区二区三区免费看 | 91影视成人 | 91色在线观看视频 | 日韩字幕在线观看 | 国产人免费人成免费视频 | 免费观看黄 | 久久精品国产成人精品 | 在线观看精品一区 | 亚洲黄色成人 | 天天干中文字幕 | 国产精品v欧美精品 | 97精品国产91久久久久久 | 国产精品小视频网站 | 国产破处精品 | 日韩视频在线观看视频 | 久久久久久国产精品免费 | 中文字幕电影高清在线观看 | 91精品欧美一区二区三区 | 波多野结衣最新 | 中文字幕一区二区三区在线观看 | 久久精品精品电影网 | 欧美亚洲国产日韩 | 国产精品不卡一区 | 丁香婷婷在线 | 麻豆91精品| 私人av | 日本久久精品视频 | 在线观看一 | 亚洲精品在线观看免费 | 日韩欧美在线第一页 | 日韩成人免费在线 | 亚洲综合国产精品 | 婷婷丁香在线观看 | a久久久久久 | 九九热精品视频在线播放 | 五月天久久综合网 | 国产免费片| 深夜视频久久 | 日韩在线观看一区 | 在线va网站| 美女精品在线 | 精品欧美一区二区精品久久 | 日日噜噜噜噜夜夜爽亚洲精品 | 在线观看一级视频 | 在线观看国产一区二区 | 大荫蒂欧美视频另类xxxx | 超碰人人乐 | 久久久久久久久久久成人 | 久久线视频 | 国产一区二区在线免费 | 丁香五月网久久综合 | 国产精品久久久久久吹潮天美传媒 | 91精品国产麻豆国产自产影视 | 国产精品成人一区二区 | 一区二区三区免费在线观看视频 | 97高清视频| www黄| 午夜精品视频在线 | 人人玩人人添人人澡超碰 | 欧美久久影院 | 东方av在| 在线免费观看欧美日韩 | 天堂黄色片 | 四虎影视8848aamm | 黄色高清视频在线观看 | 五月婷婷激情六月 | 国产成人精品三级 | 中文字幕黄色网 | 日韩在线视频观看免费 | 超碰在线人 | av一区二区在线观看中文字幕 | 正在播放五月婷婷狠狠干 | 久久综合久久八八 | 97精品视频在线播放 | 久久综合色播五月 | 日本激情视频中文字幕 | 91av亚洲 | 日日碰狠狠躁久久躁综合网 | 色网站免费在线看 | 亚洲电影一区二区 | 久久综合色婷婷 | 久久久久亚洲天堂 | 看片一区二区三区 | 欧美成人在线免费观看 | 久久久久久久毛片 | 在线视频国产区 | 国产在线专区 | 天天视频色版 | 久久精品国产亚洲精品2020 | 国产精品久久久久久一区二区 | 久久精品国产亚洲 | 精品自拍sae8—视频 | 成人va在线观看 | 超碰最新网址 | 国产黄影院色大全免费 | 久久人人爽人人片 | 黄色的视频网站 | 久草国产精品 | 99视频在线观看一区三区 | 日韩免费一区二区 | 成人在线免费观看网站 | 日本中文字幕在线一区 | 久久精品久久精品久久精品 | 最近日本中文字幕 | 国产精彩视频一区 | 成人网色 | 午夜av电影 | 亚洲日本色 | 中文在线a天堂 | 福利视频一区二区 | 四虎国产精品免费观看视频优播 | 久久综合九色综合97婷婷女人 | 国产一区二区视频在线播放 | 国产涩涩网站 | 九九视频在线播放 | 永久免费的av电影 | 成人禁用看黄a在线 | av一级久久 | 久久手机免费观看 | 91视频免费看 | 成人在线观看免费视频 | 91麻豆精品国产自产在线游戏 | 久久综合五月天 | 一区二区三区四区精品视频 | 久草在线视频首页 | 天天色天天综合 | 久久国产精品视频观看 | 99精品区 | 日本激情视频中文字幕 | 久久综合久久综合这里只有精品 | 国产91影院 | 日韩欧美aaa | 中国一级特黄毛片大片久久 | 久久久久久久久久久免费 | 69国产精品视频 | 天天综合成人 | 日本精品视频在线播放 | 黄色av观看 | 成人免费一区二区三区在线观看 | 国产成人亚洲在线观看 | 精品国产伦一区二区三区观看方式 | 国产免费不卡av | 91黄色视屏 | 日韩小视频 | 久久草视频 | 91手机电视| 草久热| 蜜桃麻豆www久久囤产精品 | 少妇bbb | 国产黄色免费在线观看 | 成人午夜av电影 | 日韩欧美视频免费在线观看 | 久久久影院官网 | 免费久久久久久 | 最新精品国产 | 国产精品午夜在线观看 | 波多野结衣在线观看一区二区三区 | 青青河边草免费观看完整版高清 | 青青久草在线 | av大全免费在线观看 | www.久久久.cum | 中文字幕乱码亚洲精品一区 | 97在线精品视频 | 超碰97在线看 | 日韩动态视频 | 91成熟丰满女人少妇 | 免费aa大片| 韩日精品在线观看 | 亚洲美女在线国产 | 天天操人人干 | 久久国产精品99国产 | 免费www视频 | 色吊丝在线永久观看最新版本 | 成人在线视频在线观看 | 不卡的av电影在线观看 | 久久99国产精品视频 | av不卡网站 | 国产亚洲欧美精品久久久久久 | 欧美一级片免费播放 | 日韩欧美一区二区三区免费观看 | 日韩在线观看视频中文字幕 | 日韩在线三区 | 国产精品麻豆果冻传媒在线播放 | 伊人婷婷久久 | 在线高清 | 亚洲最新av网址 | 欧美综合久久久 | 久久久久亚洲a | 色综合激情网 | 碰超在线97人人 | 日产av在线播放 | 日韩欧美在线综合网 | 国产不卡在线播放 | 一级黄色片在线免费看 | 国产精品久久久久久久久久三级 | 中文字幕日韩精品有码视频 | 人人爽人人舔 | 视频成人永久免费视频 | 99国产一区| 香蕉视频网站在线观看 | 99热99re6国产在线播放 | 中文字幕一区在线观看视频 | 在线观看中文字幕视频 | 国产精品久久久久四虎 | 人人艹视频 | 麻豆视频在线免费观看 | av色网站 | 久久久精品免费观看 | 999精品在线| 欧美激情综合色综合啪啪五月 | 中文字幕韩在线第一页 | 国产不卡一区二区视频 | 美女av在线免费 | 激情婷婷网 | 天天射天天爱天天干 | 国产精品亚 | av怡红院 | 青青草国产精品视频 | 99精品系列| 深爱婷婷网 | 久久久久一区 | 国产免费成人 | 色婷婷激婷婷情综天天 | 伊人色播 | 一二三精品视频 | 97av在线视频 | 夜色资源站国产www在线视频 | 亚洲午夜av电影 | 日韩三级av | 国产成人一级电影 | www.av中文字幕.com | 三级在线播放视频 | 在线视频免费观看 | 久久www免费视频 | 亚洲天天综合 | www.伊人网| 日韩精品一区二区三区在线视频 | 国产精品a久久久久 | 亚洲天天摸日日摸天天欢 | 亚洲婷婷在线视频 | 高清一区二区三区av | 六月婷操 | 日韩在线观看视频在线 | 国产精品永久久久久久久www | 午夜精品久久久久久久99水蜜桃 | 成人黄色视 | 国产成人综合精品 | 久久久2o19精品 | 色噜噜噜噜 | 三日本三级少妇三级99 | 国产又粗又硬又爽视频 | 最近中文字幕久久 | 日本黄色大片免费看 | 欧美日韩高清在线观看 | 黄色国产高清 | 国产1区2区3区精品美女 | 久久综合狠狠综合久久综合88 | 在线观看日韩中文字幕 | 九九热免费视频在线观看 | 中文在线字幕观看电影 | a天堂中文在线 | 日韩在线欧美在线 | 日韩三级视频在线观看 | 特级毛片在线 | 综合色综合色 | 成人国产一区二区 | 91精品在线免费观看视频 | 婷婷av网站 | 超碰97人人爱 | www.久久免费视频 | 男女精品久久 | 久久久久免费精品国产 | 日韩成片| 亚洲少妇影院 | 国产 一区二区三区 在线 | 人人超碰免费 | 日韩免费在线观看视频 | 正在播放 国产精品 | 一区二区视频免费在线观看 | 午夜精品一二三区 | 午夜精品成人一区二区三区 | 黄色亚洲大片免费在线观看 | 亚州精品在线视频 | www视频在线播放 | 国产99久久99热这里精品5 | www.色国产| 99精品国产99久久久久久97 | 亚洲综合小说 | 国产精品专区在线 | 国产一二区精品 | 久久一区二区三区国产精品 | 一级免费黄视频 | 精品国产成人在线影院 | 亚洲一区不卡视频 | 国产免费午夜 | 亚洲成年人在线播放 | 国产精品免费观看久久 | 亚洲精品国产成人av在线 | 99热最新 | 婷婷久久网 | 国产精品美女999 | 99热最新| 91中文字幕 | www.色在线| 香蕉视频网址 | 久久免费看毛片 | 黄色激情网址 | 97精品国产97久久久久久免费 | 国产精品1区2区3区在线观看 | 一区二区三区精品久久久 | 少妇高潮冒白浆 |