一、斷點上傳辨義
在 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 內容檢索
(未完待續)