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

ASP.NET使用SQL Server FileTable流式播放视频时遇内存不足异常

解决FileTable视频流式传输中的System.OutOfMemoryException异常

看起来你在通过ASPX的VideoHandler.ashx流式传输FileTable中的视频时踩了内存溢出的坑——这个问题我之前帮很多开发者解决过,核心原因是你用了错误的流处理方式,而且没有实现浏览器视频播放必备的分段请求支持。

问题根源分析

你当前用StreamReader来读取视频文件,这本身就不对:StreamReader是为处理文本文件设计的,用它处理视频这种二进制文件会引入编码问题,更关键的是,你的代码逻辑看起来是想把整个视频文件加载到内存里再传输——视频文件动辄几十上百MB,甚至几个GB,一次性加载必然触发OutOfMemoryException,尤其是并发请求多的时候,内存直接就爆了。

完整解决方案

下面是经过验证的修复方案,核心是改用二进制流处理+支持浏览器的Range分段请求,分块传输数据,彻底避免内存溢出。

1. 替换StreamReader为FileStream,实现分段流式传输

把你的Handler代码改成下面这样,我已经加了详细注释:

public void ProcessRequest(HttpContext context)
{
    string fullpath = "你的FileTable视频文件路径"; // 注意:这里要从数据库获取正确的FileTable文件路径
    long size, start, end, length;

    // 用FileStream处理二进制文件,支持共享读取(避免文件被锁)
    using (FileStream fs = new FileStream(fullpath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
    {
        size = fs.Length;
        string rangeHeader = context.Request.Headers["Range"];

        // 处理浏览器的Range请求(视频播放时浏览器会分段请求文件)
        if (!string.IsNullOrEmpty(rangeHeader))
        {
            // 解析Range头,格式一般是 "bytes=0-1023"
            string[] rangeParts = rangeHeader.Replace("bytes=", "").Split('-');
            start = long.Parse(rangeParts[0]);
            // 如果Range头只给了起始位置,就默认到文件末尾
            end = rangeParts.Length > 1 && !string.IsNullOrEmpty(rangeParts[1]) ? long.Parse(rangeParts[1]) : size - 1;
            length = end - start + 1;

            // 返回206 Partial Content状态码,告诉浏览器这是分段数据
            context.Response.StatusCode = 206;
            context.Response.AddHeader("Content-Range", $"bytes {start}-{end}/{size}");
        }
        else
        {
            // 没有Range请求时返回整个文件
            start = 0;
            end = size - 1;
            length = size;
            context.Response.StatusCode = 200;
        }

        // 设置正确的响应头,关键是Content-Type要匹配你的视频格式
        context.Response.ContentType = "video/mp4"; // 比如video/avi、video/mov,根据实际文件调整
        context.Response.AddHeader("Content-Length", length.ToString());
        context.Response.AddHeader("Accept-Ranges", "bytes"); // 告诉浏览器支持分段请求
        context.Response.AddHeader("Cache-Control", "public, max-age=86400"); // 可选:添加缓存策略,减轻服务器压力

        // 分块读取并传输数据,用4KB缓冲区(可根据服务器性能调整大小)
        byte[] buffer = new byte[4096];
        fs.Seek(start, SeekOrigin.Begin);
        long bytesSent = 0;

        while (bytesSent < length)
        {
            // 每次读取不超过缓冲区大小,也不超过剩余需要发送的字节数
            int bytesRead = fs.Read(buffer, 0, (int)Math.Min(buffer.Length, length - bytesSent));
            if (bytesRead == 0) break;

            context.Response.OutputStream.Write(buffer, 0, bytesRead);
            context.Response.Flush(); // 立即把数据发送给浏览器,不要在内存里攒着
            bytesSent += bytesRead;
        }
    }

    context.Response.End();
}

2. 确保FileTable文件路径的正确性

从FileTable获取文件路径时,一定要用SQL Server提供的GetFileNamespacePath()方法,避免手动拼接路径出错:

SELECT file_stream.GetFileNamespacePath() AS VideoFilePath 
FROM YourFileTable 
WHERE YourVideoId = @VideoId; -- 替换成你的表名和ID参数

同时要确保ASP.NET应用程序池的身份账号(比如IIS AppPool\YourPoolName)有访问FileTable对应文件系统目录的权限。

3. 额外优化建议

  • 避免一次性加载文件:绝对不要用File.ReadAllBytes()这类方法把整个视频读入内存,哪怕是小文件也不行,并发起来必炸。
  • 调整缓冲区大小:如果服务器性能较好,可以把缓冲区从4KB调到8KB或16KB,提升传输效率;如果服务器内存紧张,保持4KB即可。
  • 限制并发请求:如果你的网站访问量很大,可以在IIS里设置应用程序池的并发请求限制,或者在Handler里加简单的限流逻辑,防止内存被占满。

常见坑点排查

  • 检查Content-Type是否正确:如果类型不对,浏览器可能无法识别视频,甚至会下载文件而不是播放。
  • 确认文件没有被锁:用FileShare.ReadWrite打开文件,避免其他进程(比如SQL Server)占用文件导致读取失败。
  • 测试Range请求:可以用Postman模拟发送Range: bytes=0-1023请求,看返回的状态码是不是206,以此验证分段传输是否正常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 09:39:57