学习HTTP Range
HTTP Range 请求
一种通过指定文件字节范围加载部分数据的技术,广泛用于断点续传、流媒体播放、分布式文件系统的数据分片加载等场景。
请求格式-在请求头中使用 Range
字段指定所需的字节范围
Range: bytes=0-1023// bytes=0-1023:表示请求文件的第 0 到第 1023 字节。
// bytes=1024-:表示从第 1024 字节开始到文件末尾。
// bytes=-1024:表示请求最后 1024 字节。
服务器响应-服务器会返回 206 Partial Content 状态码,并包含 Content-Range
响应头
HTTP/1.1 206 Partial Content
Content-Range: bytes 0-1023/1234567// bytes 0-1023/1234567:表示当前返回的范围和文件的总大小(单位:字节)
前端实现文件分片加载
<!DOCTYPE html>
<html><head><meta charset="utf-8" />
</head><body><!-- 创建一个带有控制条和静音属性的视频播放器 --><video id="video" controls muted></video><script>const video=document.querySelector('video'); // 获取页面中的 video 元素// 视频的 URL 可通过浏览器直接访问【检查视频路径正确无误】;服务器支持 HTTP Range 请求【在服务器配置中启用 Accept-Ranges: bytes 支持】,如果不支持,fetch 请求会失败const assetURL='xxxxxxx.mp4'; // 定义视频文件的 URLconst mimeCodec='video/mp4; codecs="avc1.64001F, mp4a.40.2"'; // 定义视频的 MIME 类型和编解码器if('MediaSource' in window&&MediaSource.isTypeSupported(mimeCodec)) { // 检查浏览器是否支持 MediaSource API 和指定的 MIME 类型const mediaSource=new MediaSource(); // 创建一个新的 MediaSource 对象video.src=URL.createObjectURL(mediaSource); // 将 MediaSource 对象绑定到 video 元素mediaSource.addEventListener('sourceopen',() => { // 监听 sourceopen 事件,表示 MediaSource 已经准备好const sourceBuffer=mediaSource.addSourceBuffer(mimeCodec); // 添加一个 SourceBuffer 来处理指定 MIME 类型的数据let loadedBytes=0; // 记录已加载的字节数const segmentSize=1024*1024; // 每段大小 1MB // 定义每次请求的分段大小为 1MBlet totalSize=0; // 在此处声明并初始化 totalSize // 初始化视频总大小为 0function fetchSegment (start) { // 定义一个函数用于按分段下载视频数据const end=start+segmentSize-1; // 计算当前分段的结束位置fetch(assetURL,{headers: {Range: `bytes=${start}-${end}`}, // 发送 HTTP 请求,指定请求的字节范围}).then(response => {if(response.ok) {// 提取总大小(仅在第一次加载时获取)if(!totalSize&&response.headers.get('Content-Range')) { // 如果还没有获取到总大小,则从响应头中提取const contentRange=response.headers.get('Content-Range');totalSize=parseInt(contentRange.split('/')[1],10); // 解析 Content-Range 头以获取总大小}return response.arrayBuffer(); // 将响应体转换为 ArrayBuffer 格式}throw new Error(`Failed to fetch segment: ${response.status}`); // 如果请求失败则抛出错误}).then(data => {sourceBuffer.appendBuffer(data); // 将下载的数据追加到 SourceBuffer 中loadedBytes+=data.byteLength; // 更新已加载的字节数sourceBuffer.addEventListener('updateend',() => { // 监听 updateend 事件,表示数据已经成功追加if(loadedBytes<totalSize) {fetchSegment(loadedBytes); // 如果还有未加载的数据,则继续加载下一个分段} else {mediaSource.endOfStream(); // 如果所有数据都已加载完毕,则结束流video.play(); // 开始播放视频}},{once: true}); // 确保该事件只触发一次}).catch(console.error); // 捕获并打印任何错误}fetchSegment(0); // 开始加载第一个片段 // 调用 fetchSegment 函数开始加载第一个分段});} else {console.error('Unsupported MIME type or codec: ',mimeCodec); // 如果不支持指定的 MIME 类型或编解码器,则输出错误信息}function sourceOpen (_) { // 定义 sourceOpen 回调函数const mediaSource=this; // 将 this 绑定到 mediaSource 变量const sourceBuffer=mediaSource.addSourceBuffer(mimeCodec); // 添加一个 SourceBuffer 来处理指定 MIME 类型的数据let loadedBytes=0; // 已加载的字节数 // 初始化已加载的字节数let totalSize=null; // 视频总大小 // 初始化视频总大小const segmentSize=4096; // 每段大小 (假设 4KB) // 定义每次请求的分段大小为 4KBlet isAppending=false; // 标记是否正在追加数据sourceBuffer.addEventListener('updateend',() => { // 监听 updateend 事件,表示数据已经成功追加isAppending=false; // 标记不再追加数据if(loadedBytes<totalSize) {const nextStart=loadedBytes; // 计算下一个分段的起始位置const nextEnd=Math.min(nextStart+segmentSize-1,totalSize-1); // 计算下一个分段的结束位置fetchAndAppend(nextStart,nextEnd); // 加载并追加下一个分段的数据} else {mediaSource.endOfStream(); // 如果所有数据都已加载完毕,则结束流video.play(); // 开始播放视频}});function fetchAndAppend (start,end) { // 定义一个函数用于按分段下载视频数据if(isAppending) return; // 避免重复加载 // 如果正在追加数据,则直接返回isAppending=true; // 标记正在追加数据fetch(assetURL,{headers: {Range: `bytes=${start}-${end}`}, // 发送 HTTP 请求,指定请求的字节范围}).then(response => {if(response.status===206) { // 如果服务器支持范围请求,则继续处理const contentRange=response.headers.get('Content-Range'); // 获取 Content-Range 响应头if(contentRange&&!totalSize) {// 解析 Content-Range: bytes start-end/totalconst match=contentRange.match(/\/(\d+)$/); // 使用正则表达式解析 Content-Range 头if(match) {totalSize=parseInt(match[1],10); // 解析并设置视频总大小}}return response.arrayBuffer(); // 将响应体转换为 ArrayBuffer 格式}throw new Error('Range request not supported'); // 如果服务器不支持范围请求,则抛出错误}).then(data => {sourceBuffer.appendBuffer(data); // 将下载的数据追加到 SourceBuffer 中loadedBytes+=data.byteLength; // 更新已加载的字节数}).catch(error => console.error('Fetch error:',error)); // 捕获并打印任何错误}// 初始加载第一个片段fetchAndAppend(0,segmentSize-1); // 调用 fetchAndAppend 函数开始加载第一个分段}</script>
</body></html>
总结
- 客户端通过
Range
请求加载指定字节范围。 - 服务器返回 206 Partial Content 响应。
- 使用
MediaSource
动态拼接分段数据,实现无缝播放
相关文章:

学习HTTP Range
HTTP Range 请求 一种通过指定文件字节范围加载部分数据的技术,广泛用于断点续传、流媒体播放、分布式文件系统的数据分片加载等场景。 请求格式-在请求头中使用 Range 字段指定所需的字节范围 Range: bytes0-1023// bytes0-1023:表示请求文件的第 0 …...

大语言模型训练的数据集从哪里来?
继续上篇文章的内容说说大语言模型预训练的数据集从哪里来以及为什么互联网上的数据已经被耗尽这个说法并不专业,再谈谈大语言模型预训练数据集的优化思路。 1. GPT2使用的数据集是WebText,该数据集大概40GB,由OpenAI创建,主要内…...
Webpack和Vite的区别
一、构建速度方面 webpack默认是将所有模块都统一打包成一个js文件,每次修改都会重写构建整个项目,自上而下串行执行,所以会随着项目规模的增大,导致其构建打包速度会越来越慢 vite只会对修改过的模块进行重构,构建速…...

【再谈设计模式】模板方法模式 - 算法骨架的构建者
一、引言 在软件工程、软件开发过程中,我们经常会遇到一些算法或者业务逻辑具有固定的流程步骤,但其中个别步骤的实现可能会因具体情况而有所不同的情况。模板方法设计模式(Template Method Design Pattern)就为解决这类问题提供了…...

Bytebase 3.1.1 - 可定制的快捷访问首页
🚀 新功能 可定制的快捷访问首页。 支持查询 Redis 集群中所有节点。 赋予项目角色时,过期时间可以定义精确到秒级的时间点。 🔔 重大变更 移除 Database 消息里的实例角色信息。调用 GetInstance 或 ListInstanceRoles 以获取实例角色信息…...

Java阶段四04
第4章-第4节 一、知识点 CSRF、token、JWT 二、目标 理解什么是CSRF攻击以及如何防范 理解什么是token 理解什么是JWT 理解session验证和JWT验证的区别 学会使用JWT 三、内容分析 重点 理解什么是CSRF攻击以及如何防范 理解什么是token 理解什么是JWT 理解session验…...
B2C API安全警示:爬虫之外,潜藏更大风险挑战
在数字化时代,B2C(Business-to-Consumer)电子商务模式已成为企业连接消费者、推动业务增长的重要桥梁。而B2C API(应用程序编程接口)作为企业与消费者之间数据交互的桥梁,其安全性更是至关重要。然而&#…...

OCR文字识别—基于PP-OCR模型实现ONNX C++推理部署
概述 PaddleOCR 是一款基于 PaddlePaddle 深度学习平台的开源 OCR 工具。PP-OCR是PaddleOCR自研的实用的超轻量OCR系统。它是一个两阶段的OCR系统,其中文本检测算法选用DB,文本识别算法选用CRNN,并在检测和识别模块之间添加文本方向分类器&a…...

如何播放视频文件
文章目录 1. 概念介绍2. 使用方法2.1 实现步骤2.2 具体细节3. 示例代码4. 内容总结我们在上一章回中介绍了"如何获取文件类型"相关的内容,本章回中将介绍如何播放视频.闲话休提,让我们一起Talk Flutter吧。 1. 概念介绍 播放视频是我们常用的功能,不过Flutter官方…...

MySQL -- 约束
1. 数据库约束 数据库约束时关系型数据库的一个重要功能,主要的作用是保证数据的有效性,也可以理解为数据的正确性(数据本身是否正确,关联关系是否正确) 人工检查数据的完整性工作量非常大,在数据库中定义一些约束,那么数据在写入数据库的时候,就会帮我们做一些校验.并且约束一…...

php 使用simplexml_load_string转换xml数据格式失败
本文介绍如何使用php函数解析xml数据为数组。 <?php$a <xml><ToUserName><![CDATA[ww8b77afac71336111]]></ToUserName><FromUserName><![CDATA[sys]]></FromUserName><CreateTime>1736328669</CreateTime><Ms…...

net-http-transport 引发的句柄数(协程)泄漏问题
Reference 关于 Golang 中 http.Response.Body 未读取导致连接复用问题的一点研究https://manishrjain.com/must-close-golang-http-responsehttps://www.reddit.com/r/golang/comments/13fphyz/til_go_response_body_must_be_closed_even_if_you/?rdt35002https://medium.co…...

高级软件工程-复习
高级软件工程复习 坐标国科大,下面是老师说的考试重点。 Ruby编程语言的一些特征需要了解要能读得懂Ruby程序Git的基本命令操作知道Rails的MVC工作机理需要清楚,Model, Controller, View各司什么职责明白BDD的User Story需要会写,SMART要求能…...
eslint.config.js和.eslintrc.js有什么区别
eslint.config.js 和 .eslintrc.js 的主要区别在于它们所对应的 ESLint 版本和配置方法: 1. .eslintrc.js: 这是 ESLint v8 及更早版本使用的配置文件格式。 它使用层级式的配置系统。 现在被称为"旧版"配置格式 。 2. eslint.config.js&am…...
如何使用MVC模式设计和实现校园自助点餐系统的微信小程序
随着智慧校园的普及,校园自助点餐系统在提高学生用餐效率、减轻食堂运营压力方面发挥了重要作用。 在开发这类系统时,MVC(Model-View-Controller)模式是一种非常适合的架构,它将系统的业务逻辑、用户界面和数据交互清晰…...

继续坚持与共勉
经过期末考试后,又要开始学习啦。 当时一直在刷算法题就很少写博客了,现在要继续坚持写博客,将每天对于题的感悟记录下来。 同时我将会在学习Linux操作系统,对于过去学习的内容进行回顾!! 在此ÿ…...
人机交互 | 期末复习(上)| 补档
文章目录 📚1-HCI Introduction🐇人机交互的定义,分别解释人-机-交互的概念🐇six ”mantras“ of UCD🐇Difference between User-Interface (UI) and User-Experience(UX)📚2-HCI history🐇WIMP🐇WYSIWYG📚3-Understanding User🐇Design Thinking Process的…...

Oracle 表分区简介
目录 一. 前置知识1.1 什么是表分区1.2 表分区的优势1.3 表分区的使用条件 二. 表分区的方法2.1 范围分区(Range Partitioning)2.2 列表分区(List Partitioning)2.3 哈希分区(Hash Partitioning)2.4 复合分…...

多并发发短信处理(头条项目-07)
1 pipeline操作 Redis数据库 Redis 的 C/S 架构: 基于客户端-服务端模型以及请求/响应协议的 TCP服务。客户端向服务端发送⼀个查询请求,并监听Socket返回。通常是以 阻塞模式,等待服务端响应。服务端处理命令,并将结果返回给客…...
网络编程的进程查看连接描述符信息等
一.查看当前进程的socket对应的fd信息 1. lsof lsof(List Open Files)命令可以列出系统中所有打开的文件的信息,包括 socket。 用法 要查看特定进程的 socket 信息,可以使用以下命令: lsof -p <pid> | grep…...
Android Wi-Fi 连接失败日志分析
1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分: 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析: CTR…...

linux之kylin系统nginx的安装
一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源(HTML/CSS/图片等),响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址,提高安全性 3.负载均衡服务器 支持多种策略分发流量…...
进程地址空间(比特课总结)
一、进程地址空间 1. 环境变量 1 )⽤户级环境变量与系统级环境变量 全局属性:环境变量具有全局属性,会被⼦进程继承。例如当bash启动⼦进程时,环 境变量会⾃动传递给⼦进程。 本地变量限制:本地变量只在当前进程(ba…...

JavaScript 中的 ES|QL:利用 Apache Arrow 工具
作者:来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗?了解下一期 Elasticsearch Engineer 培训的时间吧! Elasticsearch 拥有众多新功能,助你为自己…...
线程同步:确保多线程程序的安全与高效!
全文目录: 开篇语前序前言第一部分:线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分:synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分ÿ…...

SCAU期末笔记 - 数据分析与数据挖掘题库解析
这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...

LeetCode - 394. 字符串解码
题目 394. 字符串解码 - 力扣(LeetCode) 思路 使用两个栈:一个存储重复次数,一个存储字符串 遍历输入字符串: 数字处理:遇到数字时,累积计算重复次数左括号处理:保存当前状态&a…...
蓝桥杯 2024 15届国赛 A组 儿童节快乐
P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡,轻快的音乐在耳边持续回荡,小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下,六一来了。 今天是六一儿童节,小蓝老师为了让大家在节…...

2.Vue编写一个app
1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...
c++ 面试题(1)-----深度优先搜索(DFS)实现
操作系统:ubuntu22.04 IDE:Visual Studio Code 编程语言:C11 题目描述 地上有一个 m 行 n 列的方格,从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子,但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...