音视频文件提供流式传输之HTTP Range 请求

在 Web 开发中,正确返回音频和视频流给前端的方式是确保服务器端以流的形式发送媒体文件,而不是将整个文件加载到内存中,然后再传输。这种做法可以提高性能,避免内存溢出,尤其是在处理大文件时。

对于音频和视频流的处理,最常见的技术是 HTTP 流式传输(HTTP Streaming) Range 请求。这些方法允许客户端(浏览器或播放器)按需请求并接收音频和视频的部分内容,而不是一次性加载整个文件。

要在后端处理包含 Range 头的请求,首先需要理解 Range 请求头是如何工作的。客户端通过 Range 请求头向服务器指定请求的字节范围。服务器根据 Range 请求返回相应的数据片段,通常用于视频、音频或大文件的流式传输。

1. Range 请求头的工作原理

客户端请求某个文件的部分内容时,会在 HTTP 请求头中包含 Range,例如:

复制代码
Range: bytes=0-1023

表示客户端请求文件的第 0 字节到第 1023 字节(共 1024 字节)。

如果客户端想请求多个范围(例如文件的两个不连续部分),则可以使用如下格式:

复制代码
Range: bytes=0-1023, 2048-3071

2. 后端处理 Range 请求头

在 C# 中,后端需要从请求头中获取 Range 的信息,并且根据该信息返回对应的字节范围。通常可以通过 HttpRequest.Headers 来获取请求头中的 Range

示例代码(ASP.NET Core):
复制代码
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Http;
using System.IO;
using System.Linq;
using System.Net.Http.Headers;

[Route("api/[controller]")]
public class MediaController : ControllerBase
{ 
    [HttpGet("audio")]
    public IActionResult GetAudio(string filename)
    {
        var fileInfo = new FileInfo(filename);
        if (!fileInfo.Exists)
        {
            return NotFound();
        }

        var fileLength = fileInfo.Length;
        var rangeHeader = Request.Headers["Range"].ToString();

        if (string.IsNullOrEmpty(rangeHeader))
        {
            return BadRequest("Invalid Range header.");
        }

        // 解析 Range 请求头
        var rangeMatch = System.Text.RegularExpressions.Regex.Match(rangeHeader, @"bytes=(\d+)-(\d+)?");
        if (!rangeMatch.Success)
        {
            return BadRequest("Invalid Range header.");
        }

        var start = long.Parse(rangeMatch.Groups[1].Value);
        var end = rangeMatch.Groups[2].Success ? long.Parse(rangeMatch.Groups[2].Value) : fileLength - 1;

        // 确保范围合法
        if (start < 0 || end >= fileLength || start > end)
        {
            return BadRequest("Invalid range.");
        }

        // 读取文件的指定范围
        var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
        var contentRange = new RangeHeaderValue(start, end);
        var contentLength = end - start + 1;

        // 设置响应头
        Response.Headers.Add("Content-Range", $"bytes {start}-{end}/{fileLength}");
        Response.Headers.Add("Accept-Ranges", "bytes");
        Response.StatusCode = StatusCodes.Status206PartialContent; // 206 Partial Content

        fileStream.Seek(start, SeekOrigin.Begin);

        return File(fileStream, "audio/mpeg", enableRangeProcessing: true);
    }
}

解释:

  1. Range 请求头解析

    • Request.Headers["Range"] 获取 Range 请求头的内容。
    • 使用正则表达式 @"bytes=(\d+)-(\d+)?" 来提取请求的开始和结束字节。
    • 如果 end 字段缺失,则将 end 设置为文件的最后一个字节。
  2. 合法性检查

    • 确保 startend 在合法范围内,即它们不能超出文件的总长度,也不能发生 start > end 的情况。
  3. 文件流处理

    • 使用 FileStream 打开文件,并设置 Seek 方法从指定的字节位置开始读取文件内容。
    • 返回部分文件内容时,设置响应头 Content-Range,告知客户端返回的数据范围。
  4. 返回部分内容

    • 设置响应状态码为 206 Partial Content,表示这是一个部分内容的响应。
    • 使用 File 方法返回文件流,并通过 enableRangeProcessing: true 告诉 ASP.NET Core 进行范围处理。

3. 前端使用 <audio> 标签播放音频

在前端使用 <audio> 标签时,浏览器会自动发出 Range 请求,只要服务器支持 Range 请求,并且文件能够分段提供。你不需要特别配置客户端来处理 Range,浏览器会自己处理。

示例:基础 <audio> 标签用法
复制代码
<html>
    <head>
        <title>audio page</title>
    </head>
    <body>
        <audio controls>
            <source src="http://127.0.0.1:9099/api/media/audio?filename=1.mp3" type="audio/mp3">
        </audio>
    </body>
</html>
相关推荐
鲜花飘飘扬1 小时前
HTTP请求头中表示代理IP地址的属性及获取情况
网络协议·tcp/ip·http
weixin_495248401 小时前
短剧视频翻译配音指南:古装、甜宠与悬疑如何本地化?
音视频
禹亮科技2 小时前
制造业100㎡跨国技术评审会议室音视频整体解决方案(亿联+思必驰+讯飞AI落地实践)
人工智能·音视频·视频会议系统集成
潜创微科技2 小时前
HDMI 分配器芯片方案商 IT66630 有源分配芯片方案
音视频
不爱记笔记3 小时前
多模态AI如何理解视频内容?从视觉、语音到语义的三层技术拆解
人工智能·自然语言处理·nlp·音视频·多模态
paopaokaka_luck4 小时前
基于springboot3+vue3的智能文库平台(AI智能搜索、AI智能汇总、实时在线状态展示、多格式文档预览与富文本编辑、Echarts图形化分析)
前端·网络·spring boot·网络协议·echarts
论迹复利5 小时前
Sniff 省电模式参数调优——续航与音频稳定性平衡实操
音视频·sniff·bt·功耗、
发光小北6 小时前
SG-RS232/485/422-Fiber-110 串口光纤收发器如何使用?
网络协议
星核0penstarry7 小时前
从 Dialog-RSN-1 看语音 Agent 走向:企业如何评估音频原生模型与 API 服务
人工智能·音视频·音频·api
M158227690558 小时前
工业跨网段通信零改造方案!SG-NAT-210/410 NAT 网关全功能解析,不改 PLC IP 实现多协议互通
网络·网络协议·tcp/ip