using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using DiuDiuTemplate.Domain.WebHelpers.Base; using DiuDiuTemplate.Infrastructure.Common.Helpers; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.WebUtilities; using Microsoft.Net.Http.Headers; namespace DiuDiuTemplate.UserApi.Controllers { public class FileUpLoadController : ApiControllerBase { private IWebHostEnvironment _webhostEnv; public FileUpLoadController(IWebHostEnvironment webhostEnv) { _webhostEnv = webhostEnv; } /// <summary> /// 上传文件到API服务器 较小的文件上传 20M /// </summary> /// <returns>返回文件的saveKeys</returns> [HttpPost] [RequestSizeLimit(20971520)] [AllowAnonymous] public async Task<List<string>> UploadFile(List<IFormFile> files) { List<string> saveKeys = new List<string>(); foreach (var formFile in files) { //相对路径 string saveKey = GetRelativePath(formFile.FileName); //完整路径 string path = GetAbsolutePath(saveKey); await WriteFileAsync(formFile.OpenReadStream(), path); saveKeys.Add(saveKey); } return saveKeys; } /// <summary> /// 大文件上传到API服务器 /// 流式接收文件,不缓存到服务器内存/// </summary> /// <att name="DisableRequestSizeLimit">不限制请求的大小</att> /// <returns>返回文件的saveKeys</returns> [HttpPost] [Route("BigFileUpload")] [DisableRequestSizeLimit] [AllowAnonymous] public async Task<List<string>> BigFileUpload() { List<string> saveKeys = new List<string>(); //获取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(); //读取 section 每个formData算一个 section 多文件上传时每个文件算一个 section while (section != null) { var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition); if (hasContentDispositionHeader) { if (contentDisposition.IsFileDisposition()) { //相对路径 string saveKey = GetRelativePath(contentDisposition.FileName.Value); //完整路径 string path = GetAbsolutePath(saveKey); await WriteFileAsync(section.Body, path); saveKeys.Add(saveKey); } else { string str = await section.ReadAsStringAsync(); } } section = await reader.ReadNextSectionAsync(); } return saveKeys; } /// <summary> /// 写文件导到磁盘 /// </summary> /// <param name="stream">流</param> /// <param name="path">文件保存路径</param> /// <returns></returns> private static async Task<int> WriteFileAsync(System.IO.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; } /// <summary> /// 获取相对路径 /// </summary> /// <param name="fileName"></param> /// <returns></returns> private string GetRelativePath(string fileName) { string str1 = CommonHelper.GetGuid().Replace("-", ""); int start = fileName.LastIndexOf('.'); //相对路径 return $"/upload/{DateTime.Now.Year}/{DateTime.Now.Month}/{DateTime.Now.Day}/{str1}{fileName.Substring(start, fileName.Length - start)}"; } /// <summary> /// 获取完整路径 /// </summary> /// <param name="relativePath"></param> /// <returns></returns> private string GetAbsolutePath(string relativePath) { //完整路径 string path = Path.Join(_webhostEnv.WebRootPath, relativePath); string directoryPath = Path.GetDirectoryName(path); if (!Directory.Exists(directoryPath)) { Directory.CreateDirectory(directoryPath); } return path; } } }
小文件上传和大文件上传;
最后记得要添加静态文件中间件
app.UseStaticFiles();
API默认没有这个中间件
还可以设置一些上传大小的限制,这个只是程序的配置,如果部署到IIS等,还需要修改IIS的上传文件限制,这里只实现代码部分;
services.Configure<FormOptions>(x =>
{
x.ValueLengthLimit = int.MaxValue;
x.MultipartBodyLengthLimit = int.MaxValue;
x.MultipartHeadersLengthLimit = int.MaxValue;
});