把大象装进冰箱:HTTP传输大文件的方法

上次我们谈到了HTTP报文里的div,知道了HTTP可以传输很多种类的数据,不仅是文本,也能传输图片,音频和视频。

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

相比之下,100M的光纤固网或者4G移动网络在这些大文件的压力下都变成了小水管,无论是上传还是下载,都会把网络传输链路挤的满满当当。

所以如何在有限的带宽下高效快捷的传输这些大文件就成了一个重要的课题,这就好比是已经打开了冰箱门,该怎么把大象塞进去在关上门呢?

下面我们就一起看看HTTP协议里有哪些手段能解决这个问题。

数据压缩

通常浏览器在发送请求时都会带着Accept-Encoding头字段,里面是浏览器支持的压缩格式,例如gzip,deflate等,这样服务器就可以从中选择一种压缩算法,放进Content-Encoding响应头里,在把原数据压缩后发给浏览器。

如果压缩率能有50%,也就是说100k的数据能够压缩成50k的大小,那么就相当于在带宽不变的情况下网速提升了一倍,加速的效果是非常明显的。

不过这个解决方法也有个缺点,gzip等压缩算法通常只对文本文件有较好的压缩率,而图片,音频视频等多媒体数据本身已经是高度压缩的,在用gzip处理也不会变小,所以它就失效了。

不过数据压缩在处理文本的时候效果还是很好的,所以各大网站的服务器都会使用这个手段作为保底,例如,在nginx里就会使用gzip on指令,启用对text/html的压缩。

分块传输

在数据压缩之外,还能有什么办法来解决大文件的问题呢?

压缩是把大文件整体变小,我们可以反过来思考,如果大文件整体不能变小,那就把它拆开,分解成多个小块,把这些小块分批发给浏览器,浏览器收到后在组装复原。

这样浏览器和服务器都不用在内存里保存文件的全部,每次只收发一小部分,网络也不会被大文件长时间占用,内存,带宽等资源也就节省下来了。

这种化整为零的思路在HTTP协议里就是chunked分块传输编码,在响应报文里用头字段Transfer-Encoding:chunked来表示,意思是报文里的div部分不是一次性发过来的,而是分成了许多的块(chunk)逐个发送。

分块传输也可以用于流式数据,例如有数据库动态生成的表单页面,这种情况下div数据的长度是未知的,无法在头字段Content-Length给出确切的长度,所以也只能用chunked方式分块发送。

Transfer-Encoding:chunked和Content-Length这两个字段是互斥的,也就是说响应报文里这两个字段不能同时出现,一个响应报文里的传输要么是长度已知,要么是长度未知。

下面是分块传输的编码规则:

1.每个分块包含两个部分,长度头和数据块

2.长度头是以CRLF结尾的一行明文,用16进制数字表示长度

3.数据块紧跟在长度头后,最后也用CRLF结尾,但数据不包含CRLF

4.最后用一个长度为0的块表示结束

范围请求

有了分块传输编码,服务器就可以轻松收发大文件了,但对于上G的超大文件,还有一些问题需要考虑。

比如,你在看当下正热播的电视剧,想跳过片头,直接看正片,或者有段剧情无聊,想拖动进度条快进几分钟,这实际上是想获取一个大文件其中的片段数据,而分块传输并没有这个能力。

HTTP协议为了满足这样的需求,提出了范围请求的概念,允许客户端在请求头里使用专用字段来表示只获取文件的一部分,相当于是客户端的化整为零。

范围请求不是web服务器必备的功能,可以实现也可以不实现,所以服务器必须在响应头里使用字段Accept-Ranges:bytes明确告知客户端:我是支持范围请求的。

请求头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的意思差不多,但表示div只是原数据的一部分

第三,服务器要添加一个响应头字段Content-Range,告诉片段的实际偏移量和资源的总大小,格式是bytes x-y/length,与Range头区别在没有“=”,范围后多了总长度

最后剩下的就是发送数据了,直接把片段用TCP发送给客户端,一个范围请求就算是处理完了。

多段数据

刚才说的范围请求一次只获取一个片段,其实它还支持在Range头里使用多个"x-y",一次性获取多个片段数据。

这种情况需要使用一种特殊的MIME类型:multipart/byteranges,表示报文的div是由多段字节序列组成的,并且还要用一个参数boundary=xxx给出段之间的分隔标记。

每一个分段必须以"--boundary"开始,之后要用Content-Type和Content-Range标记这段数据的类型和所在范围,然后就像普通的响应头一样以回车换行结束,在加上分段数据,最后用一个"--boundary--"表示所有的分段结束。

原文地址:https://www.cnblogs.com/sjks/p/11180193.html

时间: 2024-10-30 12:00:27

把大象装进冰箱:HTTP传输大文件的方法的相关文章

Linux 下EXT2文件系统 —— 如何将蚂蚁和大象优雅的装进冰箱里

这一阵子真是偷懒,无时无刻不和自己身体中的懒癌做斗争.最终我还是被打败了,星期天两天几乎都是荒废过去的,在空闲的时候实际上我内心也是有点焦虑的,不知道去怎么度过这时间.学习吧又不想学习,看电视娱乐吧也没有心情.在这种状态中,我度过了星期天,到了今天我才趴在窗扇看了一会linux.这一部分之前也看过,不过是走马观花式的看,并没有投入精力.今天看了下,发现其实windows的文件系统和linux还是有很多不同的.从设计的角度上,linux的文件系统似乎更加聪明,看完这部分,我也更喜欢linux了.

[杂谈]把大象放进冰箱里!

把大象放进冰箱里! 一 数学家的办法把大象放到冰箱里的分析学方法1)先把大象微分,然后把它放到冰箱里,再在冰箱里把它积分.2)重新定义冰箱或者大象的测度(如Radon测度).3)用Banach-Tarski定理.把大象放到冰箱里的代数学方法1)先证明大象的每一部分都可以放到冰箱里.2)再证明冰箱对加法封闭.把大象放到冰箱里的拓扑学方法1)让大象把冰箱吞了,再把冰箱从里到外翻出来.2)把冰箱做成Klein瓶.把大象放到冰箱里的线性代数方法1)把大象的基先放进去,再在冰箱里张成空间.2)把大象作奇异

个人第一个开源分布式项目distributeTemplate的实现三 网络通讯netty传输大文件

今天 我将讲讲网络通讯,这里我初始版本 由于采用的事Netty框架  所以 这里讲网络Netty在我们这里是怎么使用的,下周开始添加rpc lucene内容了 实现之后的0.2 0.3版本,后面将会去掉netty依赖 采用原生的NIO2 (aio) 异步非阻塞方式 实现自己网络通讯,也就是说 这部分可能会实现一个简单的但是比netty精简高效的网络框架,后期做出来 可能会单独开一个分支开源出来,netty说白了 就是 事件驱动 以及 NIO 加一些协议 以及 异常 处理,废话不多说了. 我最近

基于RMI服务传输大文件的完整解决方案

基于RMI服务传输大文件,分为上传和下载两种操作,需要注意的技术点主要有三方面,第一,RMI服务中传输的数据必须是可序列化的.第二,在传输大文件的过程中应该有进度提醒机制,对于大文件传输来说,这点很重要,因为大文件的传输时间周期往往比较长,必须实时告知用户大文件的传输进度.第三,针对大文件的读取方式,如果采用一次性将大文件读取到byte[]中是不现实的,因为这将受制于JVM的可用内存,会导致内存溢出的问题. 笔者实验的基于RMI服务传输大文件的解决方案,主要就是围绕以上三方面进行逐步解决的.下面

C# Socket传输大文件

1.基础类TransferFiles,client和server都需要 using System; using System.Collections.Generic; using System.Text; using System.Net; using System.Net.Sockets; using System.Windows.Forms; namespace Server { public class TransferFiles { public static int SendData(

OpenAdaptor1——基于webservice传输大文件

<span style="font-family:Arial, Helvetica, sans-serif;BACKGROUND-COLOR: rgb(255,255,255)">OpenAdaptor支持XFire和CXF开发的webservice,所以开发CXF webservice,基于myeclipse开发比较容易.参照网上webrvice传输大文件示例修改 http://blog.csdn.net/kongxx/article/details/7540930<

远程传输大文件使用什么平台好呢?

远程传输大文件使用什么平台好呢?小文件倒是还可以通过QQ这样的方式进行传输,但是它对传输文件的大小有所限制,传输大文件就行不通了. 远程传输大文件使用什么平台好呢?传输大文件一个是要求传输稳定,不能说传了一点就断了,人又不可能一直盯着看是不是正常传输,这会造成传输时间的浪费.再一个要求文件传输的速度不能太慢,既然是大文件传输,速度很慢可能就要花好几个小时,甚至一整天,这会延误文件的传达. 我平时是使用网盘进行大文件的传输,网盘传输的速度比一般的通讯工具要更加快,也更加稳定,还没有文件的限制. 比

TCP协议传输大文件读取时候的问题

TCP协议传输大文件读取时候的问题 大文件传不完的bug 我们在定义的时候定义服务端每次文件读取大小为10240, 客户端每次接受大小为10240 我们想当然的认为客户端每次读取大小就是10240而把客户端的读下来的文件想当然大小每一次都加上10240 而实际上服务端发送文件send每次发送不一定是一次性把10240的文件传送完,可能分了好几次进行发送至缓冲区这我们实际文件大小就不一定是10240 解决办法: 1.对于每次服务端所发送的文件内容及大小都发送给客户端,让客户端一一对应读取 2.实时

Linux培训教程 linux系统下分割大文件的方法

在linux中分割大文件,比如一个5gb日志文件,需要把它分成多个小文件,分割后以利于普通的文本编辑器读取. 有时,需要传输20gb的大文件,Linux培训 教程件到另一台服务器,也需要把它分割成多个文件,这样便于传输数据. 以下通过五个不同的例子,来讲解Linux下分割大文件的方法,供大家参考. 例1.以每个文件1000行分割 split命令分割文件成每个文件1000行,并且文件名依次为 [前缀]aa,[前缀]ab, [前缀]ac等,默认的前缀是X,每个文件的行数为1000行. 命令: 复制代