一、断点上传辨义
在 Http 协议之下的 文件下载有“断点续传”功能,从服务器到客户端的文件下载可行,而对Http 协议的文件上传则不同。
断点续传是基于 请求-响应 模式的文件传输方式,客户端通过记录不同数据流位置来实现,还必须知道整个文件的大小才能实现断点续传。客户端保存传送状态是必要条件,无论是文件上传还是下载。
对于象 FlashGet/NetAnt 这样的下载软件还提供了多线程并行下载的功能以提高下载速度。
对于上传文件,服务器端不能主动进行请求(由于防火墙屏蔽),所以只能由客户端进行分段发送,而服务器端接收的结果也未必能够反馈回客户端(在互联网中异常情况会随处出现),客户端无法知道服务器端接收了多少数据,只能整个重发。所以断点上传这个概念是不可行的,分段/分组上传才是正解。
如果还不清楚请参照 《断点续传原理》
1.1附:断点续传原理
所谓断点续传,也就是要从文件已经下载的地方开始继续下载。所以在客户端浏览器传给 Web服务器的时候要多加一条信息--从哪里开始。 下面是用自己编的一个"浏览器"来传递请求信息给Web服务器,要求从2000070字节开始。
GET /down.zip HTTP/1.0 User-Agent: NetFox RANGE: bytes=2000070- Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
仔细看一下就会发现多了一行RANGE: bytes=2000070- 这一行的意思就是告诉服务器down.zip这个文件从2000070字节开始传,前面的字节不用传了。
服务器收到这个请求以后,返回的信息如下:
206 Content-Length=106786028 Content-Range=bytes 2000070-106786027/106786028 Date=Mon, 30 Apr 2001 12:55:20 GMT ETag=W/"02ca57e173c11:95b" Content-Type=application/octet-stream Server=Microsoft-IIS/5.0 Last-Modified=Mon, 30 Apr 2001 12:55:20 GMT
和前面服务器返回的信息比较一下,就会发现增加了一行:
Content-Range=bytes 2000070-106786027/106786028
返回的代码也改为206了,而不再是200了。
知道了以上原理,就可以进行断点续传的编程了。
二、分段上传
2.1逻辑流程
大文件拆分 --> 发送传输计划(告知服务器以下这些片段是一个文件)
是否需要从服务器端申请一个文件ID,如果以文件 Hash作为文件标识可以不用申请ID
--> 分段发送并记录哪些片段是已经发送过的
--> 服务器分段接收 -->全部片段接收完成 --> 组合成一个文件 --> 文件校验
--> 标记为已经完成 ,可以提供下载
2.2如何分段
对于压缩文件 可以 按照单个文件进行发送,但是缺乏通用性,所以还是按照 统一的片段大小进行拆分。
拆分后如何标记片段呢?通过计算 片段的 Hash 值 在客户端进行记录,这样即使客户端关机或软件退出,下次启动后仍然可以继续上传。
2.3分段的策略
文件分成几段是一个讲究策略的事情,根据网速/服务器端的负载情况,很难找到一个最优解,那么较优化的解决方法还是有的。
分段的大小取决于网速和稳定性,另外Web服务器也存在一定的限制,如果没有权限修改 IIS 的配置,那么就只能使用一个比较小的分段大小。
在局域网中传输 10M左右的文件不会占用太多的时间,用户真正关心的并不是占用了多少流量而是用了多少时间,长时间停滞的上传进度会让人费解,无法知道后台在搞些什么。
而太过细碎的分片又会造成带宽的浪费,和重复的响应等待。
比如 每段大小限制在1M以内,如果文件比较小,比如2M以内,就不必分段。
2.4文件Hash
通过 计算Hash 的方法 可以唯一标识一个文件,如果文件内容在分段上传过程中发生变化,那么需要重新计算文件Hash,同时服务器端会抛弃已接收到的内容,重新接收分段。
数据流的Hash 可以通过 MD5 或者 SHA1 进行计算,在 .Net 中有现成的方法。
文件Hash 和 片段 Hash 可以对接收到的文件内容进行验证。所以客户端和服务器端需要相同的 Hash 方法。
文件比较和版本检查
对于一个大小超过 100M的文件来说,不会全部都有改变,整个文件重新发送会造成不必要的流量浪费。
为了减少不必要的流量浪费,可以从服务器申请一个文件上传的Id,该ID 对应唯一的一个文件,如果文件发生变化,根据各个片段Hash 分析那一部分发生了变化,重新发送变化的部分。文件片段全部传输完成后,发送整个文件的Hash供服务器进行校验进行片段合并。
如果文件的变化发生在开始部分,哪怕只是增加了一个字节,也不可避免重新发送整个文件。这又涉及到另外一个论题,即《文件比较》
rsync 文档中提到两种 Hash 的算法 rolling checksum(32bits),md5 checksume(128bits)
这里记录一下,以后可能会有用。
问题 文件比较
参见 rsync unix 下文件同步的 核心算法
2.5 文件打包
通常用户在传输文件时会进行压缩,但是对于服务器端分解并展示压缩文件内容来说效率比较低下,如果不提供内容目录和预览用户体验会比较差,简单的说还不如一个FTP 系统来的直接。
云端要提供一个文件压缩包内容目录和预览可以有两种解决方法,
2.5.1 客户端程序负责打包
客户端程序按照文件目录进行打包并提供内容的目录列表和生成预览图,这些内容按照一定的格式打包在压缩文件中。
此种模式 可以参考 OpenXPS 文档格式说明
其实 Windows 平台中有很多这样的例子,Office 2007 以及后续版本使用的 docx,xlsx 等文件内容都是这种类型。
从.Net 3.0 开始支持 System.IO.Packaging.Package 类
Package 为一个抽象类,可用于将对象组织到定义的物理格式的单个实体中,从而实现可移植性与高效访问。
ZIP 文件是 Package 的主物理格式。其他 Package 实现可以使用其他物理格式(如 XML 文档、数据库或 Web 服务)。
与文件系统类似,在分层组织的文件夹和文件中引用 Package 中包含的项。
PackageRelationship(“关系”)定义源 Package 或 PackagePart 与目标对象之间的关联。
XpsDocument 基于 Package 体系结构,是一个包类型,旨在基于开放 XML Paper Specification (XPS) (XML 纸张规范 (XPS))存储文档。
默认情况下,Microsoft .NET Framework 使用包来为使用标准 ZIP 文件格式的页面和文档存储内容、资源和关系。与任何 ZIP 文件一样,应用程序可使用 System.IO.Packaging 类在单个可高效访问的容器中存储和选择性保护任何类型和数量的数据文件。
有关更多信息,请参见“Open Packaging Conventions (OPC) specification”(开放式打包约定 (OPC) 规范)。`
在一个包装文件中不仅有原始数据对象,还保存了一定的关系数据,这些数据通过 XML 文件的形式保存在包装文件中,这部分内容可以在服务器端进一步处理保存在数据库中以便进行检索查询。
2.5.2 服务器端解析压缩文件
如果客户端提交的仅仅是一个普通的压缩文件,那么服务端就需要进行加工处理。
可能需要进行处理的内容:
- 解析 压缩文件的目录结构,提取文件列表。
- 对可识别的文件提取摘要内容和生成预览图
好了,我知道了,这些好像和分段上传没什么关系,要另开一个专题了。
三、传输协议
有人说 HTTP 协议是一个又大又笨的协议,对于大文件上传一点也不好用,这一点我非常赞同。但是好处是可以兼容客户端程序和网页程序。
通常网盘通过 443 (HTTPS) 协议进行加密传输。对于企业内网可以不采用这种方法。
3.1 多线程分片上载
如果没有多线程分片上载文件,如何提高传输速度。
多线程并不复杂,但是由于多数用户是用的是 xDSL 非对称速率的线路,那么下载带宽会远远大于上传带宽,也就是说多线程上载未必会提高传输速度。
对于服务器端需要支持并允许同一用户多个连接进行上传。
3.3 BITS
后台智能传输服务(Background Intelligent Transfer Service)
BITS 好像就是专门为这个专题准备的,但是似乎并不被各种应用采纳。更多的用于服务器和服务器的数据同步。
客户环境的复杂性使得这种专门的服务无法广泛采用。
其中还是有很多可以借鉴的内容,比如 带宽的保留,客户端资源占用和自动恢复连接等。要知道,如果预计客户传输文件的规模每次都有几百兆,那么所有细节都会成为问题。
目的
后台智能传输服务(BITS)客户端和服务器之间传输文件(下载或上传),并提供有关转让的进度信息。您也可以从同行中下载文件。
在适用的情况下 应用程序需要使用BITS:
- 在前台或后台异步传输文件。
- 保留其他网络应用程序的响应性。
- 后自动恢复文件传输网络断开连接,并重新启动计算机。
开发者受众
BITS是专为C和C + +开发人员。
运行时的要求
BITS 4.0版本包含在Windows 7和Windows Server 2008 R2操作系统。
四、服务器端技术
4.1 存储
- 存储容量的预估
- 服务器端存储容量不够用的时候怎么办呢?
- 每个用户能够提供多少存储容量?
- 服务器是否支持在线的磁盘扩容?
- 如果客户规模再增加是否支持云存储(多服务器存储)?
- 有没有便宜而且容量够大的存储方案。
我坚信大容量存储的要诀就是,一次放好,不要乱动。几百个G的存储数据对于服务器来说也不是可以随便挪动地方的。如果存储规划一开始没有细心的设计,那么将来必然会惹来大麻烦。
传统的企业级存储方案通常根据规模采用 服务器联机存储、磁盘阵列、磁盘库。通常是几台台服务器拖着一个光纤磁盘柜,这已经是很高端的设备了,速度很快,也很贵。
好了,这个问题先不谈,我主要想说的是文件如何存放的形式,如何建立索引和存储摘要信息。
问题在于是否支持版本控制,如果不支持版本,问题相对比较简单。
打包文件以文件形式存放,但必须重新命名,否则,会有命名冲突。即不同的用户上传了相同名称的文件,当然也可以增加顺序后缀,但不如直接用文件 Hash码作为名称。
文件目录和索引存储在数据库中
数据表如下
文件ID 文件名 HashCode 提交日期 提交用户(作者) 文件长度 备注
文件内容
文件ID 包含文件名 长度 文件类型 摘要信息
文件摘要和缩略图 的存储
对于文档文件 如 doc、html、pdf 等文件 可以在后台解析并提取其文本内容作为摘要信息,可以全部提取,也可以根据样式提取其中的目录信息或者前100个字符或者第一段正文的内容,这个算法有待实践检验。
对于大小超过一定范围的图片文件可以生成缩略图,小图片则直接展示。
对于比较大的文件解析工作会占用大量资源,特别是内存资源,所以必须放到服务器后台进程进行操作,可以制定文件解析任务,利用夜间服务器比较空闲的时段进行作业。
如何判断服务器处于空闲
这个似乎有些复杂,对于现代多核CPU来说,所有CPU 都忙碌起来似乎是比较少见的事情,而内存空间往往也不会都被占用。通常会用 Performance API WMI 使用 系统提供的性能计数器来判断。CLR 中提供了 System.Diagnostics.PerformaceCounter 用于监控性能
利用Windows系统的任务调度服务,建立独立的进程进行处理,好处是,这些任务一旦处理完成,就会退出,完全释放资源,不会有任何麻烦。
那么另外一种方法就是 尽可能控制资源占用的数量,比如只有一个计算线程,内存占用控制在100M以内,使用 Sleep 方法,在繁忙的处理过程中稍微休息一下。
或者使用并行算法,让任务尽快完成。
4.2 Web 浏览功能
4.3 内容检索
(未完待续)