上次我们谈到了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. Android Camera2 拍照(二)——使用TextureView

    原文:Android Camera2 拍照(二)--使用TextureView 上一篇博文简单介绍了使用Camera2 API拍摄照片,并使用SurfaceView作为预览界面.实际上,相对于Surf ...

  2. 从PRISM开始学WPF(八)导航Navigation?

    原文:从PRISM开始学WPF(八)导航Navigation? 0x6Navigation Basic Navigation Prism中的Navigation提供了一种类似导航的功能,他可以根据用户 ...

  3. siliverlight某些事件无法响应

    对一些无法响应的时间,需要注册 控件名:XZWT_TreeViewItem 事件:this.XZWT_TreeViewItem_MouseLeftButtonDown 具体注册方法: XZWT_Tre ...

  4. Delphi Android 将Google ZXing 整合(调用Jar文件)

    前篇文章介绍了在delphi App(以下简称App)中可使用intent来调用Google ZXing 条码扫描器(以下简称zx),其各有优缺点,优点是我们不需关注zx本身的细节,只需调用其接口即可 ...

  5. 在IE浏览器 使用PHPExcel导出文件时时 文件名中文乱码

    1.当我们使用IE内核的浏览器下在PHPExcel报表时(谷歌.火狐浏览器正常, IE浏览器,360浏览器的兼容模式报错),会出现如下错误: 2.解决办法: 在下载文件时,对当前的浏览器进行判断, 如 ...

  6. 十七 bootstrap-table tableExport 导出xlsx格式表格

    原文:十七 bootstrap-table tableExport 导出xlsx格式表格 在[十六.bootstrap-table javascript导出数据]中,打开导出的表格时,总会弹出一个提示 ...

  7. 微信小程序把玩(二十五)loading组件

    原文:微信小程序把玩(二十五)loading组件 loading通常使用在请求网络数据时的一种方式,通过hidden属性设置显示与否 主要属性: wxml <!----> <butt ...

  8. 微信小程序把玩(十四)button组件

    原文:微信小程序把玩(十四)button组件 button按钮用的算是最普遍的组件之一. 主要属性: wxml <!--按钮默认样式,点击事件--> <button type=&qu ...

  9. PostgreSQL在win7上安装详细步骤

    原文:PostgreSQL在win7上安装详细步骤 PostgreSQL安装: 一.windows下安装过程 安装介质:postgresql-9.1.3-1-windows.exe(46M),安装过程 ...

  10. 关于SetLocaleInfo()

    原文:关于SetLocaleInfo() 此函数用于设置系统的一些本地信息, 非常有用. 比如日期格式为'yyyy/mm/dd'时, 稍微不注意,有些程序语句会报错. 以下资料网络收集: 1. Set ...