HTTP第14讲——HTTP传输大文件的方法

news2024/11/25 13:12:03

背景

HTTP 可以传输很多种类的数据,不仅是文本,也能传输图片、音频和视频。
早期互联网上传输的基本上都是只有几 K 大小的文本和小图片,现在的情况则大有不同。网页里包含的信息实在是太多了,随随便便一个主页 HTML 就有可能上百 K,高质量的图片都以 M 论,更不要说那些电影、电视剧了,几 G、几十 G 都有可能。

相比之下,100M 的光纤固网或者 4G 移动网络在这些大文件的压力下都变成了“小水管”,无论是上传还是下载,都会把网络传输链路挤的“满满当当”。
如何在有限的带宽下高效快捷地传输这些大文件就成了一个重要的课题。这就好比是已经打开了冰箱门(建立连接),该怎么把大象(文件)塞进去再关上门(完成传输)呢?
我们就一起看看 HTTP 协议里有哪些手段能解决这个问题。

数据压缩

一个最基本的解决方案,那就是“数据压缩”,把大象变成小猪佩奇,再放进冰箱。
通常浏览器在发送请求时都会带着“Accept-Encoding”头字段,里面是浏览器支持的压缩格式列表,例如 gzip、deflate、br 等,这样服务器就可以从中选择一种压缩算法,放进“Content-Encoding”响应头里,再把原数据压缩后发给浏览器。
如果压缩率能有 50%,也就是说 100K 的数据能够压缩成 50K 的大小,那么就相当于在带宽不变的情况下网速提升了一倍,加速的效果是非常明显的。
不过这个解决方法也有个缺点,gzip 等压缩算法通常只对文本文件有较好的压缩率,而图片、音频视频等多媒体数据本身就已经是高度压缩的,再用 gzip 处理也不会变小(甚至还有可能会增大一点),所以它就失效了。
不过数据压缩在处理文本的时候效果还是很好的,所以各大网站的服务器都会使用这个手段作为“保底”。例如,在 Nginx 里就会使用“gzip on”指令,启用对“text/html”的压缩。

分块传输

在数据压缩之外,还能有什么办法来解决大文件的问题呢?
压缩是把大文件整体变小,我们可以反过来思考,如果大文件整体不能变小,那就把它“拆开”,分解成多个小块,把这些小块分批发给浏览器,浏览器收到后再组装复原。
这样浏览器和服务器都不用在内存里保存文件的全部,每次只收发一小部分,网络也不会被大文件长时间占用,内存、带宽等资源也就节省下来了。
这种“化整为零”的思路在 HTTP 协议里就是“chunked”分块传输编码,在响应报文里用头字段“Transfer-Encoding: chunked”来表示,意思是报文里的 body 部分不是一次性发过来的,而是分成了许多的块(chunk)逐个发送。
这就好比是用魔法把大象变成“乐高积木”,拆散了逐个装进冰箱,到达目的地后再施法拼起来“满血复活”。
分块传输也可以用于“流式数据”,例如由数据库动态生成的表单页面,这种情况下 body 数据的长度是未知的,无法在头字段“Content-Length”里给出确切的长度,所以也只能用chunked 方式分块发送。
“Transfer-Encoding: chunked”和“Content-Length”这两个字段是互斥的,也就是说响应报文里这两个字段不能同时出现,一个响应报文的传输要么是长度已知,要么是长度未知(chunked),这一点你一定要记住。

分块传输的编码规则,其实也很简单,同样采用了明文的方式,很类似响应头

  1. 每个分块包含两个部分,长度头和数据块;
  2. 长度头是以 CRLF(回车换行,即\r\n)结尾的一行明文,用 16 进制数字表示长度;
  3. 数据块紧跟在长度头后,最后也用 CRLF 结尾,但数据不包含 CRLF;
  4. 最后用一个长度为 0 的块表示结束,即“0\r\n\r\n”。
    在这里插入图片描述

范围请求

有了分块传输编码,服务器就可以轻松地收发大文件了,但对于上 G 的超大文件,还有一些问题需要考虑。
比如,你在看当下正热播的某穿越剧,想跳过片头,直接看正片,或者有段剧情很无聊,想拖动进度条快进几分钟,这实际上是想获取一个大文件其中的片段数据,而分块传输并没有这个能力。
HTTP 协议为了满足这样的需求,提出了“范围请求”(range requests)的概念,允许客户端在请求头里使用专用字段来表示只获取文件的一部分,相当于是客户端的“化整为零”。
范围请求不是 Web 服务器必备的功能,可以实现也可以不实现,所以服务器必须在响应头里使用字段“Accept-Ranges: bytes”明确告知客户端:“我是支持范围请求的”。
如果不支持的话该怎么办呢?服务器可以发送“Accept-Ranges: none”,或者干脆不发送
“Accept-Ranges”字段,这样客户端就认为服务器没有实现范围请求功能,只能老老实实地收发整块文件了。
请求头 Range 是 HTTP 范围请求的专用字段,格式是“bytes=x-y”,其中的 x 和 y 是以
字节为单位的数据范围。
要注意 x、y 表示的是“偏移量”,范围必须从 0 计数,例如前 10 个字节表示为“0-9”,第
二个 10 字节表示为“10-19”,而“0-10”实际上是前 11 个字节。
Range 的格式也很灵活,起点 x 和终点 y 可以省略,能够很方便地表示正数或者倒数的范围。假设文件是 100 个字节,那么:
“0-”表示从文档起点到文档终点,相当于“0-99”,即整个文件;
“10-”是从第 10 个字节开始到文档末尾,相当于“10-99”;
“-1”是文档的最后一个字节,相当于“99-99”;
“-10”是从文档末尾倒数 10 个字节,相当于“90-99”。

服务器收到 Range 字段后,需要做四件事。

第一,它必须检查范围是否合法,比如文件只有 100 个字节,但请求“200-300”,这就是范围越界了。服务器就会返回状态码 416,意思是“你的范围请求有误,我无法处理,请再检查一下”。
第二,如果范围正确,服务器就可以根据 Range 头计算偏移量,读取文件的片段了,返回状态码“206 Partial Content”,和 200 的意思差不多,但表示 body 只是原数据的一部分。
第三,服务器要添加一个响应头字段 Content-Range,告诉片段的实际偏移量和资源的总大小,格式是“bytes x-y/length”,与 Range 头区别在没有“=”,范围后多了总长度。例
如,对于“0-10”的范围请求,值就是“bytes 0-10/100”。
最后剩下的就是发送数据了,直接把片段用 TCP 发给客户端,一个范围请求就算是处理完了。

有了范围请求之后,HTTP 处理大文件就更加轻松了,看视频时可以根据时间点计算出文件的Range,不用下载整个文件,直接精确获取片段所在的数据内容。
不仅看视频的拖拽进度需要范围请求,常用的下载工具里的多段下载、断点续传也是基于它实现的,要点是:
先发个 HEAD,看服务器是否支持范围请求,同时获取文件的大小;
开 N 个线程,每个线程使用 Range 字段划分出各自负责下载的片段,发请求传输数据;
下载意外中断也不怕,不必重头再来一遍,只要根据上次的下载记录,用 Range 请求剩下的那一部分就可以了。

多段数据

刚才说的范围请求一次只获取一个片段,其实它还支持在 Range 头里使用多个“x-y”,一次性获取多个片段数据。
这种情况需要使用一种特殊的 MIME 类型:“multipart/byteranges”,表示报文的 body
是由多段字节序列组成的,并且还要用一个参数“boundary=xxx”给出段之间的分隔标
记。
多段数据的格式与分块传输也比较类似,但它需要用分隔标记 boundary 来区分不同的片段,可以通过图来对比一下。
在这里插入图片描述
每一个分段必须以“- -boundary”开始(前面加两个“-”),之后要用“Content-Type”
和“Content-Range”标记这段数据的类型和所在范围,然后就像普通的响应头一样以回车
换行结束,再加上分段数据,最后用一个“- -boundary- -”(前后各有两个“-”)表示所
有的分段结束。

小结

  1. 压缩 HTML 等文本文件是传输大文件最基本的方法;
  2. 分块传输可以流式收发数据,节约内存和带宽,使用响应头字段“Transfer-Encoding:chunked”来表示,分块的格式是 16 进制长度头 + 数据块;
  3. 范围请求可以只获取部分数据,即“分块请求”,实现视频拖拽或者断点续传,使用请求头字段“Range”和响应头字段“Content-Range”,响应状态码必须是 206;
  4. 也可以一次请求多个范围,这时候响应报文的数据类型是“multipart/byteranges”,
    body 里的多个部分会用 boundary 字符串分隔。

PS:本文是观看极客之后的笔记。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/534592.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

java常用集合

java集合又是一个新世界了,从前在我刚接触java的时候,一直在纠结 集合这东西到底有啥用,后来代码写的多了。才开始学习集合 集合也叫容器。顾名思意 就是存放对象的器皿。 主要是由两大接口派生而来 :一个是 Collecton接口&#…

LeetCode高频算法刷题记录2

文章目录 1. 最大子数组和【简单】1.1 题目描述1.2 解题思路1.3 代码实现 2. 合并两个有序链表【简单】2.1 题目描述2.2 解题思路2.3 代码实现 3. 两数之和【简单】3.1 题目描述3.2 解题思路3.3 代码实现 4. 二叉树的层序遍历【中等】4.1 题目描述4.2 解题思路4.3 代码实现 5. …

代码随想录算法训练营第四十二天 | 背包问题

背包问题 理论 基础:二维 文档讲解:代码随想录 (programmercarl.com) 视频讲解:带你学透0-1背包问题!| 关于背包问题,你不清楚的地方,这里都讲了!| 动态规划经典问题 | 数据结构与算法_哔哩哔哩…

Revit SDK:ErrorHandling

前言 本文介绍 Revit 的错误处理机制。 内容 程序员对错误处理的定义和理解 程序的错误处理机制可以分为两种类型:错误返回码和异常捕捉。 错误返回码是指在程序中遇到错误时,通过函数返回值来表明错误的类型和信息。错误返回码可以在程序中被预测和…

MySQL数据落盘原理(data page、redo log、undo log、binlog、xa-2pc等源码分析)

文章目录 前言一、Innodb如何作为MySQL插件的二、page cleaner thread三、Update操作源码梳理1、生成undo log2、更新数据3、生成redo log 四、MTR与将脏页添加到Flush List1、MTR2、脏页添加到Flush List 五、事务提交1、xa-prepare2、xa-commit2.1、process_flush_stage_queu…

概率统计与计算机技术的联系及在生活当中的应用研究

title: 概率统计与计算机技术的联系及在生活当中的应用研究 date: 2023-05-16 11:42:26 tags: 题目:概率统计与计算机技术的联系及在生活当中的应用研究 摘 要 概率论与数理统计是研究随机现象统计规律性的一门学科,是理工科各专业的一门重要的基础课程…

shell免交互

shell免交互 Here Document 免交互 使用I/O重定向的方式将命令列表提供给交互式程序或命令,比如 ftp、cat 或 read 命令。 是标准输入的一种替代品可以帮助脚本开发人员不必使用临时文件来构建输入信息,而是直接就地生产出一个“文件”并用作“命令”的…

Python 爬虫(四):Selenium 框架

Selenium 是一个用于测试 Web 应用程序的框架,该框架测试直接在浏览器中运行,就像真实用户操作一样。它支持多种平台:Windows、Linux、Mac,支持多种语言:Python、Perl、PHP、C# 等,支持多种浏览器&#xff…

Android中的IPC方式

Android中的IPC方式 1.Bundle 其中Intent就是用Bundle进行传播 四大组件间的进程间通信 2.使用文件共享 2.1文件共享的缺点 无并发,适用于交换简单的数据实时性不高的情景 使用文件共享容易出: 内容不一致:如果多个线程同时写入文件&…

【JavaWeb】-- Request和Response、JSP、会话技术

文章目录 Request和Response1.概述2.Request对象2.1 Request继承体系2.2Request获取请求数据2.3 IDEA创建Servlet2.4 请求参数中文乱码问题POST请求解决方案GET请求解决方案 2.5 Request请求转发 3.Response对象3.1 Response设置响应数据功能介绍3.2 Response请求重定向3.3 路径…

RabbitMQ养成记 (8. 消费者接受消息可靠性 consumer Ack)

Consumer Ack ack 指的是acknowledge 确认, 指的是消费端收到消息后的确认方式。 有三种确认方式: 自动确认手动确认 (根据业务情况 手动确认是否成功发送)根据异常情况确认 我们在消费端用代码实践一下: 首先我们定…

不会还有人不知道软件测试报告模板怎么写吧?

在测试岗位上,写报告是一项非常重要的软技能,写得好可以加分,写不好必然减分。 但在测试岗位上,提测“通过”和提测“不通过"的软件测试报告所包含的内容是不一样的(这是个坑)。但很多测试新人可能并…

LIO-SAM UBUNTU16.04.7 ROS-KINETIC 环境 编译 安装

简单记录一下 VMWARE workstation15UBUNTU16.04.7ros-kineticgtsam4.0.0 (默认16.04比较老旧,不好用,vmtools也都没有,选了一个.7) 选16.04版本的理由也简单,只是为了参考一个博客,单独建的环…

C语言-易错点汇总

易错点汇总 指针数组和数组指针(*p).astruct stdent和typedef struct stdentF5和CtrlF5const的位置全局变量和局部变量两个相同的常量字符串数组名和&数组名数组指针定义的解读int (*arr[10])[5]数组传参(* (void (*)())0)();关于数组名是否表示整个数组和数组首元素的地址…

Java位运算

Java >>和>>>的区别 | 或: 有1得1, 全0得0 int temp a|c;System.out.println(Integer.toBinaryString(a));System.out.println(Integer.toBinaryString(c));System.out.println(Integer.toBinaryString(temp));/*** 结果输出* * 01* …

对比SQL学Python:筛选|条件判断

SQL里筛选数据主要用到了where、and、in等语句。而在Python里实现筛选的方式比较多样,用到了 与&或|非~ 逻辑运算符,和isin()函数等。我们感受一下二者的区别吧: 汇总: 类型Python语句参考单条件筛选 data[data[shop_type]A…

Python复制目录及其子目录下的所有文件到指定新目录并重命名

Python复制目录及其子目录下的所有文件到指定新目录并重命名 前言前提条件相关介绍实验环境Python复制目录及其子目录下的所有文件到指定新目录并重命名代码实现输出结果 前言 本文是个人使用Python处理文件的电子笔记,由于水平有限,难免出现错漏&#x…

最常用的界线矢量数据大合集(文末有附下载方法)

最近收集了挺多比较常用的矢量界线数据,例如地理分区、气候分区等界线数据,在日常制图、学习、科研等方面使用频率比较高。废话不多说,这里给大家分享一下!! 1、中国农业熟制区划矢量数据 2、黄土高原空间范围矢量数据…

【Leetcode -509.斐波那契数 -520.检测大写字母】

Leetcode Leetcode - 509.斐波那契数Leetcode - 520.检测大写字母 Leetcode - 509.斐波那契数 题目:斐波那契数 (通常用 F(n) 表示)形成的序列称为 斐波那契数列 。 该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。…

node笔记_koa框架是什么?

文章目录 ⭐前言⭐ koa框架是如何发展而来的?⭐ koa框架的基本使用💖 安装 koa💖 koa的Middleware示例💖 支持xml ⭐ 结束 ⭐前言 大家好,我是yma16,本文介绍node的一个web框架koa。 往期文章 node_window…