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接口調用方法: