上次我们谈到了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--"表示所有的分段结束。
 

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

  1. 把大象装进冰箱的N种方法

    作者:折剑头链接:https://www.zhihu.com/question/49214119/answer/115728034来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注 ...

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

    这一阵子真是偷懒,无时无刻不和自己身体中的懒癌做斗争.最终我还是被打败了,星期天两天几乎都是荒废过去的,在空闲的时候实际上我内心也是有点焦虑的,不知道去怎么度过这时间.学习吧又不想学习,看电视娱乐吧也 ...

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

    基于RMI服务传输大文件,分为上传和下载两种操作,需要注意的技术点主要有三方面,第一,RMI服务中传输的数据必须是可序列化的.第二,在传输大文件的过程中应该有进度提醒机制,对于大文件传输来说,这点很重 ...

  4. linux传输大文件

    http://dreamway.blog.51cto.com/1281816/1151886 linux传输大文件

  5. C# 的 WCF文章 消息契约(Message Contract)在流(Stream )传输大文件中的应用

    我也遇到同样问题,所以抄下做MARK http://www.cnblogs.com/lmjq/archive/2011/07/19/2110319.html 刚做完一个binding为netTcpBi ...

  6. WCF 用netTcpbinding,basicHttpBinding 传输大文件

    问题:WCF如何传输大文件 方案:主要有几种绑定方式netTcpbinding,basicHttpBinding,wsHttpbinding,设置相关的传输max消息选项,服务端和客户端都要设置,tr ...

  7. 使用QQ传输大文件

    现在在公网上能传输大文件并且稳定支持断点续传的软件非常少了,可以使用qq来做这件事. qq传输单个文件有时候提示不能超过4g有时候提示不能超过60g,没搞明白具体怎么样. 可以使用qq的传输文件夹功能 ...

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

    TCP协议传输大文件读取时候的问题 大文件传不完的bug 我们在定义的时候定义服务端每次文件读取大小为10240, 客户端每次接受大小为10240 我们想当然的认为客户端每次读取大小就是10240而把 ...

  9. netty接收大文件的方法

    参考:http://blog.csdn.net/linuu/article/details/51371595 https://www.jianshu.com/p/a0a51fd79f62 netty默 ...

随机推荐

  1. Windows 10开发基础——网络编程

    主要内容: HttpClient类 Socket通信 WCF通信 HttpClient类      在UWP中可以用来进行网络通信的HttpClient类有两个,System.Net.Http.Htt ...

  2. UWP ListView

    ListViewItem项填充整个宽度最大化<ListView.ItemContainerStyle> <Style TargetType="ListViewItem&qu ...

  3. 给文件右击菜单增加7-ZIP浏览功能(用注册表设置Shell调用预览命令)

    疯狂delphi delphiXE7.XE8.XE10公开课A 群号:58592705 QQ:513187410 朱建强 BAT-给文件右击菜单增加7-ZIP浏览功能 Reg给文件右击菜单增加7-ZI ...

  4. 华为虚拟机结合VMware搭建环境测试snmp

    最近在研究zabbix监控,在实际生产环节中,我们不单单是需要对linux主机进行监控还需要对网络设备防火墙等等进行监控,那么在linux主机上我们可以安装zabbix-agernt,但是在路由器交换 ...

  5. 修改用户名后TSF出现"需要本地工作区。工作区 xxx 并未驻留在本计算机上"

    解决方法就是:1,打开vs下的"开发人员命令提示"2,按下面格式输入命令:tf workspaces /collection:http://192.168.0.110:8080/t ...

  6. 使用Visual Studio开发Python

    Python优秀的集成开发环境有PyCharm,Visual Studio Code等,当然你仍然可以使用Visual Studio进行开发.如果你熟悉Visual Studio,使用Visual S ...

  7. MongoDB对文档的操作

    插入文档 db.COLLECTION_NAME.insert({doc1},{doc2},...) e.g.:db.collection.insert({name:'123',age:12},{nam ...

  8. iOS和Android使用MQTT协议实现消息推送和即时通讯(替代XMPP,已经有TMS MQTT)

    大多数应用都会涉及到即时聊天的功能,在开源方案中有XMPP和MQTT.其中XMPP是基于XML的,并且不支持消息质量QOS, 所以本身并没有消息重传的机制,从而会导致丢消息.而MQTT是基于二进制的, ...

  9. C++与QML混合编程实现2048

    http://blog.csdn.net/ieearth/article/details/42705305

  10. 今天想安装 windowsl ive 提示安装失败 错误码

    Windows Live installation error: OnCatalogResult: 0x80072ee6 看了了这个老兄的回答,试了试,果然OK,谢谢@普洛提亚从这里下载安装包,然后安 ...