You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何以非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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 18:11:05