日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

.NET Core Web APi大文件分片上传研究

發布時間:2023/12/4 asp.net 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 .NET Core Web APi大文件分片上传研究 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
【導讀】前兩天發表利用FormData進行文件上傳.NET和.NET Core Web APi FormData多文件上傳,然后有人問要是大文件幾個G上傳怎么搞,常見的不就是分片再搞下斷點續傳,動動手差不多也能搞出來,只不過要深入的話,考慮的東西還是很多。由于斷點續傳之前寫個幾篇,這里試試利用FormData來進行分片上傳。

前端文件分片處理

這里我們依然是使用FormData來上傳,只不過在上傳之前對文件進行分片處理,如下HTML代碼

<div?class="form-horizontal"?style="margin-top:80px;"><div?class="form-group"><div?class="col-md-10"><input?name="file"?id="file"?type="file"?/></div></div><div?class="form-group"><div?class="col-md-offset-2?col-md-10"><input?type="submit"?id="submit"?value="上傳"?class="btn?btn-success"?/></div></div> </div>

接下來則是上傳腳本,如下:

$(function?()?{$('#submit').click(function?()?{UploadFile($('#file')[0].files);}); });

簡單來說只需實現上述UploadFile方法,對大文件進行分片處理,然后上傳就完事,文件上傳后大致如下圖所示,最后只需將所有文件進行合并處理為目標文件即可

接下來我們詳細講講如何實現,當然重點就在于如何進行分片處理,我們拿到上傳目標文件,然后通過slice方法進行分片,在分片處理之前我們定義緩沖區大小(默認為8兆),然后循環遍歷文件大小,然后將分片數據塞入分片數組,最后利用循環或者隊列先進先出機制獲取數組分片元素上傳。

function?UploadFile(targetFile)?{//?創建上傳文件分片緩沖區var?fileChunks?=?[];//?目標文件var?file?=?targetFile[0];//?設置分片緩沖區大小var?maxFileSizeMB?=?8;var?bufferChunkSize?=?maxFileSizeMB?*?(1024?*?1024);//?讀取文件流起始位置var?fileStreamPos?=?0;//?設置下一次讀取緩沖區初始大小var?endPos?=?bufferChunkSize;//?文件大小var?size?=?file.size;//?將文件進行循環分片處理塞入分片數組while?(fileStreamPos?<?size)?{var?fileChunkInfo?=?{file:?file.slice(fileStreamPos,?endPos),start:?fileStreamPos,end:?endPos}fileChunks.push(fileChunkInfo);fileStreamPos?=?endPos;endPos?=?fileStreamPos?+?bufferChunkSize;}//?獲取上傳文件分片總數量var?totalParts?=?fileChunks.length;var?partCount?=?0;//?循環調用上傳每一片while?(chunk?=?fileChunks.shift())?{partCount++;//?上傳文件命名約定var?filePartName?=?file.name?+?".partNumber-"?+?partCount;chunk.filePartName?=?filePartName;//?url參數var?url?=?'partNumber='?+?partCount?+?'&chunks='?+?totalParts?+?'&size='?+?bufferChunkSize?+?'&start='?+?chunk.start?+?'&end='?+?chunk.end?+?'&total='?+?size;chunk.urlParameter?=?url;//?上傳文件UploadFileChunk(chunk);} }

上述關于分片塞入數組就不用再廢話,這里我們將每一片文件命名先進行一個約定(文件名+“.partNumber” + 分片號),以便所有分片上傳完成后獲取按照文件名中的分片號對其進行排序合并,這也就是合并文件的依據。接下來就是上傳每一片文件

function?UploadFileChunk(chunk)?{var?data?=?new?FormData();data.append("file",?chunk.file,?chunk.filePartName);$.ajax({url:?'/api/upload/upload?'?+?chunk.urlParameter,type:?"post",cache:?false,contentType:?false,processData:?false,data:?data,}); }

后端合并文件處理

我們可以看到在請求URL上額外加了其他參數,為什么要加上這些參數呢?主要為解決幾個問題。

其一:前端確認緩沖區大小,我們獲取前端確認的緩沖區大小,這樣后臺不用寫死,更加靈活,萬一后續進行了修改,誰知道呢?

其二:我們怎么確定文件是否已經全部上傳完了呢?在URL上我們添加分片總數和文件實際大小來完全確定文件已經全部上傳和文件完整無缺。

當然也額外添加了每一片讀取的起始位置和結束位置,若有所需也可以利用。多余的就不用我再解釋。接下來我們看看后臺如何對每一片進行處理呢?在.NET Core中實際上提供了對應APi來專門讀取FormData數據,利用Microsoft.AspNetCore.WebUtilities命名空間下的MultipartReader類。

首先我們判斷是否請求內容是否為FormData,同時通過上下文獲取上述文件讀取類的參數boundary,如下:

private?bool?IsMultipartContentType(string?contentType) {return!string.IsNullOrEmpty(contentType)?&&contentType.IndexOf("multipart/",?StringComparison.OrdinalIgnoreCase)?>=?0; }private?string?GetBoundary(string?contentType) {var?elements?=?contentType.Split('?');var?element?=?elements.Where(entry?=>?entry.StartsWith("boundary=")).First();var?boundary?=?element.Substring("boundary=".Length);if?(boundary.Length?>=?2?&&?boundary[0]?==?'"'?&&boundary[boundary.Length?-?1]?==?'"'){boundary?=?boundary.Substring(1,?boundary.Length?-?2);}return?boundary; }private?string?GetFileName(string?contentDisposition) {return?contentDisposition.Split(';').SingleOrDefault(part?=>?part.Contains("filename")).Split('=').Last().Trim('"'); }

接下來我們定義分片類而獲取URL上每一片的參數,如下:

????public?class?FileChunk{//文件名public?string?FileName?{?get;?set;?}///?<summary>///?當前分片///?</summary>public?int?PartNumber?{?get;?set;?}///?<summary>///?緩沖區大小///?</summary>public?int?Size?{?get;?set;?}///?<summary>///?分片總數///?</summary>public?int?Chunks?{?get;?set;?}///?<summary>///?文件讀取起始位置///?</summary>public?int?Start?{?get;?set;?}///?<summary>///?文件讀取結束位置///?</summary>public?int?End?{?get;?set;?}///?<summary>///?文件大小///?</summary>public?int?Total?{?get;?set;?}}

接下來在提交控制器方法上去讀取每一片數據如下

if?(!IsMultipartContentType(context.Request.ContentType)) {return?BadRequest(); }var?boundary?=?GetBoundary(context.Request.ContentType); if?(string.IsNullOrEmpty(boundary)) {return?BadRequest(); }var?reader?=?new?MultipartReader(boundary,?context.Request.Body);var?p?=?await?reader.ReadNextSectionAsync();

然后就是循環每一片(p),若不為空說明還存有分片文件,然后讀取URL上的緩沖區大小,如下:

while?(p?!=?null) {//chunk為控制器方法上類FileChunk參數var?buffer?=?new?byte[chunk.Size];var?fileName?=?GetFileName(p.ContentDisposition);//這里獲取文件名便于查找指定文件夾下所有文件chunk.FileName?=?fileName;var?path?=?Path.Combine(_environment.WebRootPath,?DEFAULT_FOLDER,?fileName);using?(var?stream?=?new?FileStream(path,?FileMode.Append)){int?bytesRead;do{bytesRead?=?await?p.Body.ReadAsync(buffer,?0,?buffer.Length);stream.Write(buffer,?0,?bytesRead);}?while?(bytesRead?>?0);}p?=?await?reader.ReadNextSectionAsync(); }

在利用內置APi讀取FormData數據時,在.NET Core 3.x會拋出如下異常:


大致原因出在.NET Core內置提供了對于參數的綁定和此方法讀取貌似有點沖突導致,我們實現如下特性移除對應綁定,然后將其添加到文件上傳方法上即可

[AttributeUsage(AttributeTargets.Class?|?AttributeTargets.Method)] public?sealed?class?DisableFormValueModelBindingAttribute?:?Attribute,?IResourceFilter {public?void?OnResourceExecuting(ResourceExecutingContext?context){var?factories?=?context.ValueProviderFactories;factories.RemoveType<FormValueProviderFactory>();factories.RemoveType<FormFileValueProviderFactory>();factories.RemoveType<JQueryFormValueProviderFactory>();}public?void?OnResourceExecuted(ResourceExecutedContext?context){} }

所有分片文件上傳完成后則是合并文件,合并的依據則是判斷URL上當前分片數和分片總數是否相等,如下:

//計算上傳文件大小實時反饋進度(TODO)//合并文件(可能涉及轉碼等) if?(chunk.PartNumber?==?chunk.Chunks) {await?MergeChunkFile(chunk); }

既然是合并文件那就需要通過分片文件名稱上末尾的分片號進行排序和拿到每一個分片文件路徑以便合并后刪除所有分片文件,所以我們定義如下類

public?class?FileSort {public?const?string?PART_NUMBER?=?".partNumber-";///?<summary>///?文件名///?</summary>public?string?FileName?{?get;?set;?}///?<summary>///?文件分片號///?</summary>public?int?PartNumber?{?get;?set;?} }

最終合并文件方法,如下:

public?async?Task?MergeChunkFile(FileChunk?chunk) {//文件上傳目錄名var?uploadDirectoryName?=?Path.Combine(_environment.WebRootPath,?DEFAULT_FOLDER,?chunk.FileName);//分片文件命名約定var?partToken?=?FileSort.PART_NUMBER;//上傳文件實際名稱var?baseFileName?=?chunk.FileName.Substring(0,?chunk.FileName.IndexOf(partToken));//根據命名約定查詢指定目錄下符合條件的所有分片文件var?searchpattern?=?$"{Path.GetFileName(baseFileName)}{partToken}*";//獲取所有分片文件列表var?filesList?=?Directory.GetFiles(Path.GetDirectoryName(uploadDirectoryName),?searchpattern);if?(!filesList.Any())?{?return;?}var?mergeFiles?=?new?List<FileSort>();foreach?(string?file?in?filesList){var?fileSize?=?new?FileInfo(file).Length;var?sort?=?new?FileSort{FileName?=?file};baseFileName?=?file.Substring(0,?file.IndexOf(partToken));var?fileIndex?=?file.Substring(file.IndexOf(partToken)?+?partToken.Length);int.TryParse(fileIndex,?out?var?number);if?(number?<=?0)?{?continue;?}sort.PartNumber?=?number;mergeFiles.Add(sort);}//?按照分片排序var?mergeOrders?=?mergeFiles.OrderBy(s?=>?s.PartNumber).ToList();//?合并文件using?var?fileStream?=?new?FileStream(baseFileName,?FileMode.Create);foreach?(var?fileSort?in?mergeOrders){using?FileStream?fileChunk?=new?FileStream(fileSort.FileName,?FileMode.Open);await?fileChunk.CopyToAsync(fileStream);}//刪除分片文件DeleteFile(mergeFiles);}public?void?DeleteFile(List<FileSort>?files) {foreach?(var?file?in?files){System.IO.File.Delete(file.FileName);} }

以上基本上實現了大文件分片處理,一些細節并未過多考慮,比如網絡問題,以及文件由于采取異步上傳,若我們通過計算所有文件大小和URL參數文件大小進行比對這會有問題,因為此時可能文件流處于緩沖區內還未持久化到磁盤,借此實現希望對有需要的童鞋提供一點思考方向。

總結

以上是生活随笔為你收集整理的.NET Core Web APi大文件分片上传研究的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。