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

.NET6中multipart/x-mixed-replace转发摄像头流异常问题排查

问题描述

控制器通过HttpClient请求摄像头RTSP流并提供给API消费者。.NET Core 2环境下所有系统运行正常,但升级到.NET6后,仅部分同虚拟机上的系统能正常显示流,其余系统无法正常工作。

已执行的排查操作
  • 禁用认证/SSL以便跟踪流
  • 用Wireshark捕获所有请求:
    • 流无法工作的系统中,中止请求后调试控制台输出的数据格式正常
    • 异常系统曾捕获到IOException: The stream was too long,说明摄像头HttpClient到输出流的复制存在缓冲问题,但仅部分系统出现该异常
  • 添加EnableRangeProcessing配置,无效果
相关代码

控制器代码

[ResponseCache(NoStore = true)]
public async Task<IActionResult> ImageStream(int cameraId, int maxfps = 30)
{
    var stream = await _cameraService.GetCameraStreamAsync(cameraId, credentials, maxfps);

    return new FileStreamResult(
        stream,
        "multipart/x-mixed-replace; boundary=\"myBoundary\"");
}

CameraService代码

public async Task<Stream> GetCameraStreamAsync(int cameraId, CameraCredentialsDto credentials, int maxFps)
{
    var client = new HttpClient { BaseAddress = new Uri($"http://{credentials.IPaddress}") };
    client.SetBasicAuthentication(credentials.Username, credentials.Password);
    client.DefaultRequestHeaders.ExpectContinue = false;

    string relativeUrl = $"cameraSpecificURL";
    return await client.GetStreamAsync(relativeUrl);
}
更新信息
  • 对比正常与异常系统的请求头,客户端请求无差异:

GET /appapi/v1/camera/1003/stream?maxfps=30&token=redacted HTTP/1.1
Host: ipaddress:5001
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9,nl;q=0.8

  • 返回数据格式正确,新段前带有双连字符
  • 异常系统直接请求摄像头流时,数据正常
解决方案建议

1. 手动复制流到响应体,避免直接返回HttpClient流

.NET6中HttpClient返回的流行为与.NET Core 2存在差异,直接返回可能引发缓冲问题。修改控制器代码:

[ResponseCache(NoStore = true)]
public async Task<IActionResult> ImageStream(int cameraId, int maxfps = 30)
{
    using var cameraStream = await _cameraService.GetCameraStreamAsync(cameraId, credentials, maxfps);
    Response.ContentType = "multipart/x-mixed-replace; boundary=\"myBoundary\"";
    Response.Headers.CacheControl = "no-cache, no-store";
    await cameraStream.CopyToAsync(Response.Body);
    return new EmptyResult();
}

同时在Program.cs中开启响应体自动刷新:

app.Use(async (context, next) =>
{
    context.Response.BodyWriter.AutoFlush = true;
    await next();
});

2. 复用HttpClient实例,避免频繁创建

当前每次请求都新建HttpClient会导致连接池耗尽,引发部分系统异常。修改CameraService使用单例HttpClient:

// Program.cs注册单例HttpClient
builder.Services.AddHttpClient<ICameraService, CameraService>();

// 修改CameraService构造函数注入
private readonly HttpClient _client;
public CameraService(HttpClient client)
{
    _client = client;
}

public async Task<Stream> GetCameraStreamAsync(int cameraId, CameraCredentialsDto credentials, int maxFps)
{
    _client.BaseAddress = new Uri($"http://{credentials.IPaddress}");
    _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", 
        Convert.ToBase64String(Encoding.ASCII.GetBytes($"{credentials.Username}:{credentials.Password}")));
    _client.DefaultRequestHeaders.ExpectContinue = false;

    string relativeUrl = $"cameraSpecificURL";
    return await _client.GetStreamAsync(relativeUrl);
}

如果多摄像头共用HttpClient,需注意每次请求前重置BaseAddress和授权头,或为每个摄像头创建命名HttpClient。

3. 禁用响应压缩

.NET6默认启用的响应压缩不适用于multipart流,会导致缓冲溢出。在Program.cs中禁用:

// 移除响应压缩中间件
// app.UseResponseCompression();

// 或针对multipart类型禁用压缩
builder.Services.Configure<ResponseCompressionOptions>(options =>
{
    options.MimeTypes = options.MimeTypes.Where(m => m != "multipart/x-mixed-replace").ToList();
});

4. 检查虚拟机资源限制

部分虚拟机可能存在CPU、内存或网络带宽限制,导致流处理时缓冲溢出。检查异常系统的资源使用率,确保分配足够的系统资源。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 04:42:06