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

.NET 5使用MultipartReader提示流已被其他组件读取异常

.NET 5 MVC流式大文件上传报流意外结束错误修复

问题表现

实现大文件流式上传功能时,执行reader.ReadNextSectionAsync()方法抛出异常:

Unexpected end of Stream, the content may have already been read by another component.(流意外结束,内容可能已被其他组件读取)

已实现DisableFormValueModelBinding过滤器移除表单相关值提供器,问题仍未解决。

对应上传接口代码

[HttpPost]
[DisableFormValueModelBinding]
[ValidateAntiForgeryToken]
public async Task<IActionResult> UploadPhysical()
{
    if (!MultipartRequestHelper.IsMultipartContentType(Request.ContentType))
    {
       ModelState.AddModelError("File",
           $"The request couldn't be processed (Error 1).");
           return BadRequest(ModelState);
        }

    var boundary = MultipartRequestHelper.GetBoundary(
       MediaTypeHeaderValue.Parse(Request.ContentType),
       _defaultFormOptions.MultipartBoundaryLengthLimit);
    var reader = new MultipartReader(boundary, HttpContext.Request.Body);
    var section = await reader.ReadNextSectionAsync();

现有DisableFormValueModelBinding过滤器代码

public void OnResourceExecuting(ResourceExecutingContext context)
{            
  var factories = context.ValueProviderFactories;
  factories.RemoveType<FormValueProviderFactory>();
  factories.RemoveType<FormFileValueProviderFactory>();
  factories.RemoveType<JQueryFormValueProviderFactory>();            
}

运行环境:.NET 5.0,Razor + MVC控制器架构。


根因

请求体流在进入接口业务逻辑前已经被其他组件读取完毕,流指针位于末尾,MultipartReader无法读取到有效分段。绝大多数同类问题由以下两点导致:

  • [ValidateAntiForgeryToken]默认从请求表单中提取防伪令牌,该逻辑执行优先级高于接口业务代码,会完整消费请求体流。
  • 全局中间件/过滤器(如请求日志、全局参数校验组件)提前读取了Request.Form或Request.Body,读取后未重置流位置。

修复步骤

  • 调整防伪令牌读取逻辑,避免触发请求体读取
    在服务配置阶段指定防伪令牌从请求头读取,不再从表单字段解析:
    services.AddAntiforgery(options =>
    {
        options.HeaderName = "X-CSRF-TOKEN";
    });
    
    前端发起上传请求时,将防伪令牌值写入X-CSRF-TOKEN请求头,不要作为表单字段提交。
  • 补全DisableFormValueModelBinding过滤器逻辑
    现有代码仅移除了值提供器工厂,未阻止框架自动读取表单,需要补充逻辑覆盖默认表单特性:
    // 先通过构造函数注入IOptions<FormOptions>获取_defaultFormOptions
    private readonly FormOptions _defaultFormOptions;
    public DisableFormValueModelBindingAttribute(IOptions<FormOptions> defaultFormOptions)
    {
        _defaultFormOptions = defaultFormOptions.Value;
    }
    
    public void OnResourceExecuting(ResourceExecutingContext context)
    {            
        var factories = context.ValueProviderFactories;
        factories.RemoveType<FormValueProviderFactory>();
        factories.RemoveType<FormFileValueProviderFactory>();
        factories.RemoveType<JQueryFormValueProviderFactory>();
        
        // 新增:重置表单特性,阻止框架自动解析表单
        context.HttpContext.Features.Set<IFormFeature>(
            new FormFeature(context.HttpContext.Request, _defaultFormOptions)
        );
    }
    
  • 排查全局组件
    检查所有全局注册的中间件、Action过滤器,若存在读取Request.Body或Request.Form的逻辑,要么移除相关逻辑,要么在读取前调用Request.EnableBuffering()开启请求缓冲,读取完成后将Request.Body.Position重置为0,保证后续组件可以正常读取流。

内容的提问来源于stack exchange,提问作者Olof84

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 20:30:45