如何以非IFormFile类型将文件从控制器传递至Mediatr处理器?
解决方案:解耦应用层与IFormFile依赖
核心思路是在WebApi控制器层完成IFormFile到通用类型的转换,让应用层仅依赖自定义的无框架绑定类型,避免引入AspNetCore相关依赖。以下是几种实用方案:
方案1:自定义文件DTO(适合中小文件)
定义一个不依赖任何WebApi特定库的文件数据传输对象,包含文件的核心元数据和内容:
// 可放在共享类库或应用层中 public class FileDataDto { public string FileName { get; set; } public string ContentType { get; set; } public byte[] Content { get; set; } }
在WebApi控制器中转换IFormFile为该DTO,再通过Mediatr传递:
[HttpPost("upload")] public async Task<IActionResult> Upload(IFormFile file, IMediator mediator) { if (file == null || file.Length == 0) return BadRequest("请上传有效文件"); using var memoryStream = new MemoryStream(); await file.CopyToAsync(memoryStream); var fileDto = new FileDataDto { FileName = file.FileName, ContentType = file.ContentType, Content = memoryStream.ToArray() }; await mediator.Send(new UploadFileCommand(fileDto)); return Ok(); }
应用层的命令和处理器完全独立于AspNetCore:
// 应用层命令 public class UploadFileCommand : IRequest<Unit> { public FileDataDto FileData { get; } public UploadFileCommand(FileDataDto fileData) => FileData = fileData; } // 应用层处理器 public class UploadFileCommandHandler : IRequestHandler<UploadFileCommand, Unit> { public async Task<Unit> Handle(UploadFileCommand request, CancellationToken cancellationToken) { // 执行业务逻辑:如保存到存储服务、解析文件内容等 var content = request.FileData.Content; var fileName = request.FileData.FileName; return Unit.Value; } }
方案2:流包装类(适合大文件,减少内存占用)
对于大文件,避免直接转换为字节数组(会占用大量内存),用自定义流包装类传递文件流及元数据:
public class FileStreamWrapper { public string FileName { get; set; } public string ContentType { get; set; } public Stream ContentStream { get; set; } }
控制器中的处理逻辑:
[HttpPost("upload-large")] public async Task<IActionResult> UploadLarge(IFormFile file, IMediator mediator) { if (file == null || file.Length == 0) return BadRequest("请上传有效文件"); var streamWrapper = new FileStreamWrapper { FileName = file.FileName, ContentType = file.ContentType, ContentStream = file.OpenReadStream() }; await mediator.Send(new UploadLargeFileCommand(streamWrapper)); return Ok(); }
应用层处理器直接操作流,注意用完后关闭流:
public class UploadLargeFileCommandHandler : IRequestHandler<UploadLargeFileCommand, Unit> { public async Task<Unit> Handle(UploadLargeFileCommand request, CancellationToken cancellationToken) { using (request.FileStream.ContentStream) { // 直接将流写入目标存储,避免内存溢出 using var targetStream = new FileStream($"storage/{request.FileStream.FileName}", FileMode.Create); await request.FileStream.ContentStream.CopyToAsync(targetStream, cancellationToken); } return Unit.Value; } }
方案3:传递临时文件路径(适合WebApi可访问本地存储的场景)
如果WebApi可以将文件临时保存到本地磁盘,直接传递文件路径和元数据给应用层:
public class FilePathInfo { public string TempFilePath { get; set; } public string FileName { get; set; } public string ContentType { get; set; } }
控制器中先保存文件到临时目录:
[HttpPost("upload-by-path")] public async Task<IActionResult> UploadByPath(IFormFile file, IMediator mediator) { if (file == null || file.Length == 0) return BadRequest("请上传有效文件"); var tempFileName = $"{Guid.NewGuid()}{Path.GetExtension(file.FileName)}"; var tempPath = Path.Combine(Path.GetTempPath(), tempFileName); using var stream = new FileStream(tempPath, FileMode.Create); await file.CopyToAsync(stream); var pathInfo = new FilePathInfo { TempFilePath = tempPath, FileName = file.FileName, ContentType = file.ContentType }; await mediator.Send(new UploadFileByPathCommand(pathInfo)); return Ok(); }
应用层处理器读取文件内容,处理完成后可删除临时文件:
public class UploadFileByPathCommandHandler : IRequestHandler<UploadFileByPathCommand, Unit> { public async Task<Unit> Handle(UploadFileByPathCommand request, CancellationToken cancellationToken) { // 读取临时文件内容 var content = await File.ReadAllBytesAsync(request.FilePathInfo.TempFilePath, cancellationToken); // 执行业务逻辑... // 可选:删除临时文件 File.Delete(request.FilePathInfo.TempFilePath); return Unit.Value; } }
内容的提问来源于stack exchange,提问作者KKKAndor
相关产品推荐
相关产品推荐

