1. 程式人生 > 其它 >NetCore-快取檔案上傳和檔案流上傳

NetCore-快取檔案上傳和檔案流上傳

NetCore官方給出的兩種檔案上傳方式分別為“緩衝”、“流式”。

1.緩衝處理:

通過模型繫結先把整個檔案儲存到記憶體,然後我們通過IFormFile得到stream,優點是效率高,缺點對記憶體要求大。檔案不宜過大。

整個檔案讀入 IFormFile,它是檔案的 C# 表示形式,用於處理或儲存檔案。 檔案上傳所用的資源(磁碟、記憶體)取決於併發檔案上傳的數量和大小。 如果應用嘗試緩衝過多上傳,站點就會在記憶體或磁碟空間不足時崩潰。 如果檔案上傳的大小或頻率會消耗應用資源,請使用流式傳輸。

2.流式處理:

直接讀取請求體裝載後的Section 對應的stream 直接操作strem即可。無需把整個請求體讀入記憶體

從多部分請求收到檔案,然後應用直接處理或儲存它。 流式傳輸無法顯著提高效能。 流式傳輸可降低上傳檔案時對記憶體或磁碟空間的需求。

(1)建立FileStreamingHelper 幫助類

相關StreamFile方法:

public static async Task<MsgDto<string>> StreamFiles(this HttpRequest request, string targetDirectory)

{

if (!MultipartRequestHelper.IsMultipartContentType(request.ContentType))

{

throw new Exception($"Expected a multipart request, but got {request.ContentType}");

}

var fileName = "";

// Used to accumulate all the form url encoded key value pairs in the

// request.

var formAccumulator = new KeyValueAccumulator();

var boundary = MultipartRequestHelper.GetBoundary(

MediaTypeHeaderValue.Parse(request.ContentType),

_defaultFormOptions.MultipartBoundaryLengthLimit);

var reader = new MultipartReader(boundary, request.Body);

var filecanvsname = "";

var itemindex = 1;

var filesection = await reader.ReadNextSectionAsync();//用於讀取Http請求中的第一個section資料

var items = new List<string>();

while (filesection != null)//遍歷

{

ContentDispositionHeaderValue contentDisposition;

var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(filesection.ContentDisposition, out contentDisposition);

/*

用於處理表單鍵值資料的section

-----------------------------99614912995

Content - Disposition: form - data; name = "SOMENAME"

Formulaire de Quota

-----------------------------99614912995

*/

//字元格式

if (MultipartRequestHelper.HasFormDataContentDisposition(contentDisposition))

{

// Content-Disposition: form-data; name="key"

//

// value

// Do not limit the key name length here because the

// multipart headers length limit is already in effect.

var key = HeaderUtilities.RemoveQuotes(contentDisposition.Name);

var encoding = GetEncoding(filesection);

using (var streamReader = new StreamReader(

filesection.Body,

encoding,

detectEncodingFromByteOrderMarks: true,

bufferSize: 1024,

leaveOpen: true))

{

// The value length limit is enforced by MultipartBodyLengthLimit

var value = await streamReader.ReadToEndAsync();

if (String.Equals(value, "undefined", StringComparison.OrdinalIgnoreCase))

{

value = String.Empty;

}

formAccumulator.Append(key.Value, value); // For .NET Core <2.0 remove ".Value" from key

filecanvsname = value;

Log4netHelper.Write("StreamFiles:(Value)" + value);

if (formAccumulator.ValueCount > _defaultFormOptions.ValueCountLimit)

{

throw new InvalidDataException($"Form key count limit {_defaultFormOptions.ValueCountLimit} exceeded.");

}

}

}

//檔案格式

else if (MultipartRequestHelper.HasFileContentDisposition(contentDisposition))

{

/*

用於處理上傳檔案型別的的section

-----------------------------99614912995

Content - Disposition: form - data; name = "files"; filename = "Misc 002.jpg"

ASAADSDSDJXCKDSDSDSHAUSAUASAASSDSDFDSFJHSIHFSDUIASUI+/==

-----------------------------99614912995

*/

if (!Directory.Exists(targetDirectory))

{

Directory.CreateDirectory(targetDirectory);

}

Log4netHelper.Write("StreamFiles:(Size)" + filesection.Body.Length);

fileName = MultipartRequestHelper.GetFileName(contentDisposition);

//獲得副檔名

string fileNameEx = Path.GetExtension(fileName);

if (!string.IsNullOrEmpty(fileNameEx)) fileNameEx = fileNameEx.ToLower();

if (!new List<string> { ".xls", ".xlsx", ".doc", ".docx", ".pdf", ".png", ".jpg", ".jpeg", ".zip", ".rar" }.Contains(fileNameEx))

{

return new MsgDto<string> { msg = "附件檔案格式有誤" };

}

var time = DateTime.Now.ToString("yyyyMMddHHmmssfff");

fileName = time + "_" + itemindex + fileNameEx;

Log4netHelper.Write("StreamFiles:(fileNameEx)" + fileName);

var loadBufferBytes = 1024;//這個是每一次從Http請求的section中讀出檔案資料的大小,單位是Byte即位元組,這裡設定為1024的意思是,每次從Http請求的section資料流中讀取出1024位元組的資料到伺服器記憶體中,然後寫入下面targetFileStream的檔案流中,可以根據伺服器的記憶體大小調整這個值。這樣就避免了一次載入所有上傳檔案的資料到伺服器記憶體中,導致伺服器崩潰。

using (var targetFileStream = System.IO.File.Create(targetDirectory + "\\" + fileName))

{

//section.Body是System.IO.Stream型別,表示的是Http請求中一個section的資料流,從該資料流中可以讀出每一個section的全部資料,所以我們下面也可以不用section.Body.CopyToAsync方法,而是在一個迴圈中用section.Body.Read方法自己讀出資料,再將資料寫入到targetFileStream

await filesection.Body.CopyToAsync(targetFileStream, loadBufferBytes);

}

items.Add(fileName);

itemindex++;

}

filesection = await reader.ReadNextSectionAsync();//用於讀取Http請求中的下一個section資料

}

var rebackurl = "";

if (items.Any())

{

var topname = items.First().Split('_')?.First() ?? "";

if (items.Count == 1)

{

foreach (var fileitem in items)

{

var fileNameEx = fileitem.Split('.').Last();

if (File.Exists(targetDirectory + "\\" + fileitem))

{

File.Move(targetDirectory + "\\" + fileitem, targetDirectory + "\\" + filecanvsname + topname + "." + fileNameEx);

rebackurl = filecanvsname + topname + "." + fileNameEx;

}

}

}

else

{

//多個檔案合併壓縮包

var fileinfos = new List<FileInfo>();

foreach (var fileitem in items)

{

if (!string.IsNullOrEmpty(fileitem))

{

var filename = targetDirectory + "\\" + fileitem;

fileinfos.Add(new FileInfo(filename));

}

}

rebackurl = ZipHelper.ZipCompress(fileinfos, targetDirectory, filecanvsname + topname);

}

}

return new MsgDto<string> { success = true, data = rebackurl, msg = "上傳成功" };

}

(2)相關呼叫方法:

建立DisableFormValueModelBindingAttribute屬性方法:

用於上傳大型檔案,以防止表單資料被讀入記憶體、防止模型繫結訪問表單資料

(3)檔案限制大小:

Startup相關ConfigureServices加上方法

//設定接收檔案長度的最大值。

services.Configure<FormOptions>(x =>

{

x.ValueLengthLimit = int.MaxValue;

x.MultipartBodyLengthLimit = int.MaxValue;

x.MultipartHeadersLengthLimit = int.MaxValue;

});

(4)iis限制檔案大小

釋出後建立web.config設定允許最大傳輸的檔案大小

webapi介面呼叫方法: