.netcore c# 文件上传大致分为两种方式,流式和缓冲式,前者适用于大文件上传,后者则适用于小文件上传,具体区别如下。
- 缓冲:通过模型绑定先把整个文件保存到内存,然后我们通过IFormFile得到Stream,优点是效率高,缺点对内存要求大,适用于小文件上传。
- 流式处理:直接读取请求体装载后的Section 对应的stream 直接操作strem即可,无需把整个请求体读入内存;适用于大文件上传。
本文只讲通过文件流上传大文件,小文件上传可以参考我的这篇文章 文件上传接口实现 - 实用工具_软件教程_.net_c#-有码挺好个人博客 (cisharp.com)
/// <summary>
/// 大文件上传,参数“softPackage”,可自定义
/// </summary>
/// <returns></returns>
[HttpPost("UploadSoftPackage")]
[RequestSizeLimit(419430400)/*文件大小上限,400M,可自定义*/]
public async Task<HttpResponseMessage> UploadSoftPackage()
{
try
{
//获取boundary
var boundary = HeaderUtilities.RemoveQuotes(MediaTypeHeaderValue.Parse(Request.ContentType).Boundary).Value ?? "";
//得到reader
var reader = new MultipartReader(boundary, HttpContext.Request.Body);
var section = await reader.ReadNextSectionAsync();
var webRoot = _hostingEnvironment.WebRootPath/*IWebHostEnvironment,用于获取网站、API根目录*/;
bool flag = false;
long fileLen = 0;
List<string> extList = new List<string>() { ".exe", ".apk", ".zip", ".rar" };
var savePath = string.Empty;
//读取section
while (section != null)
{
var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition);
if (hasContentDispositionHeader)
{
if (contentDisposition.Name == "softPackage")
{
var fileName = contentDisposition.FileName.Value;
var fileExt = Path.GetExtension(fileName);
if (extList.Contains(fileExt))
{
flag = true;
var dir = Path.Combine(webRoot, "SoftPackage");
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
savePath = $"/SoftPackage/{fileName}";
fileLen = await WriteFileAsync(section.Body, Path.Combine(dir, fileName));
}
else
{
return JsonManager.SimpleCustResponse("unsupported media type");
}
}
}
section = await reader.ReadNextSectionAsync();
}
if (!flag)
{
return JsonManager.SimpleCustResponse("softPackage is require");
}
else
{
if (fileLen > 0)
{
return JsonManager.ReturnSuccessResponse(savePath);
}
else
{
return JsonManager.SimpleCustResponse("file upload failed");
}
}
}
catch (Exception ex)
{
_logger.LogError(ex.ToString());
return JsonManager.SimpleStatusResponse(ResultCode.OPERATE_FAILED);
}
}
/// <summary>
/// 写文件导到磁盘
/// </summary>
/// <param name="stream">流</param>
/// <param name="path">文件保存路径</param>
/// <returns></returns>
private async Task<int> WriteFileAsync(Stream stream, string path)
{
const int FILE_WRITE_SIZE = 84975;//写出缓冲区大小
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;
}