如何上傳視訊加進度條
之前仿造
但無論外掛再怎麼靈活,也難以應付所有的需求,比如,你要上傳一個2G的檔案。以現在我們的網速,恐怕再快也得傳半小時。要命的是,如果你在上傳到90%的時候不小心關掉了瀏覽器,或者是手一抖摁了F5,完了,一切還得從頭再來。這種使用者體驗簡直太糟糕了。所以,斷點續傳就十分有必要了。什麼是續傳我就不解釋了,用QQ傳檔案這麼多年,大家都見過了。
這裡要說的是斷點續傳都有哪些技術要點。使用傳統的表單提交檔案或是HTML5的FormData都是將檔案“整塊”提交,服務端取到該檔案後再進行轉移、重新命名等操作,因此,無法實時儲存檔案的已上傳部分。而且在http協議下,我們無法保持瀏覽器與服務端的長連線,不能以檔案流的形式來提交。所以要解決的問題具體來講有以下幾點:
對上傳的檔案進行分割,每次只上傳一小片。服務端接收到檔案後追加到原來部分,最後合併成完整的檔案。
每次上傳檔案片前先獲取已上傳的檔案大小,確定本次應切割的位置
每次上傳完成後更新已上傳檔案大小的記錄
標識客戶端和服務端的檔案,保證不會把A檔案的內容追加到B檔案上
在參考了張鑫旭大哥的
工作原理/技術要點
首先的首先,要明確,如果我們有一個10M的檔案,每次切割上傳1M,那麼是需要發10次請求來完成的。在http協議下,只能這麼搞。斷點上傳分三步來完成:
選擇一個檔案後,獲取該檔案在伺服器上的大小,通過本地儲存或自定義的函式來獲取。
根據已上傳大小切割檔案,發出n次請求不斷向伺服器提交檔案片,服務端不斷追加檔案內容
當已上傳檔案大小達到檔案總大小時,上傳結束
首先是檔案的分割,HTML5新增了Blob資料型別,並且提供了一個可以分割資料的方法:slice(),其用法和字串、陣列的slice()方法一樣,可以擷取一個二進位制檔案的一部分。
其次是檔案片的儲存與追加,我後臺用PHP寫的,先用file_get_contents獲取檔案的二進位制格式,再用file_put_contents每次將檔案追加,具體的寫法可以參照後面,或者是下載我打包好的檔案。
接下來我們還需要實時儲存已上傳檔案的大小,以便於下次上傳前進行正確切割。使用HTML5的localStorage是一種方法,將已上傳的大小儲存在本地,下次上傳前先從本地讀取。不過這種方式是很侷限的,拋開使用者可能通過各種管家清除掉本地資料不講,假如使用者在A頁面上傳了一個檔案的50%,然後在B頁面想把該檔案上傳到另外一個地方,結果從本地一讀檔案已上傳50%了,直接從51%的位置開始上傳了,顯然是個錯誤。問題就在於本地不能存太多的資訊,通過File API只能獲取到檔案的原始名稱,無法正確的與伺服器上的檔案正確匹配。所以真正在專案中用,還得依靠服務端來儲存這些資料。
關於如何將資料存在服務端,已經前端如何取資料,我在下面會講到。
技術要點就上面的那麼多了,其實也沒有多少技術含量哈~來看看我的外掛如何使用吧。
續傳功能的使用方法
檔案的引入就不講了,可參考上一篇關於外掛的介紹。關鍵點是新增的幾個配置,先來看一下:
在服務端儲存資料
使用者在使用上傳的時候可能有各種你意想不到的操作,這裡我發揮想象描述一下使用者可能的行為:
同一臺機器使用不同帳號登入,上傳同一個檔案
檔案上傳了一部分,然後修改了檔案內容,再次上傳
檔案上傳完成100%,再次上傳該檔案
同一個頁面有多個上傳按鈕,上傳同一個檔案,或在不同頁面上傳同一個檔案
僅僅上面四條,是不是情況就夠複雜了?再加上你係統還有自己的業務邏輯,所以在服務端儲存已上傳檔案資料是非常有必要的。而且儲存資料和獲取資料的函式都交給你來定義,抱著外掛有足夠的靈活性。
因為涉及到了服務端的技術,無法演示,我將我專案中的真實使用場景在此講解一下,來展示一下如何自已定義方法來實現服務端儲存資料的可靠上傳。我定義的getUploadedSize函式如下:
檔案初始化
檔案上傳完畢的程式碼
檔案塊的處理程式碼,up6對檔案塊的處理,以及檔案續傳的邏輯進行了大幅度的優化,開發者不需要關心續傳的細節,因為up6預設就是自動續傳
我向後臺的某個地址傳送一個請求,傳遞檔名和檔案的最後修改時間為引數,後臺根據這兩個引數來找到與前臺所選擇的檔案對應的伺服器上的檔案,將伺服器返回的檔案大小return出去,來被外掛使用。為什麼要傳遞這兩個引數呢?我們在前臺無法知道伺服器上的這個檔案的名稱,所以使用原始檔名作為一個輔助標識。為了防止使用者在兩次上傳間隔修改了檔案,我們把檔案的最後修改時間也傳給服務端,讓服務端進行比較,若時間不對應則返回已上傳大小為0,重新上傳此檔案。
再來看後臺都要做哪些工作。資料庫中需要有一張表來記錄每個已檔案的情況,包含的欄位大致有:
欄位 |
描述 |
uid |
使用者ID |
id |
檔案ID標識(唯一) |
lenSvr |
伺服器檔案大小 |
lenLoc |
本地檔案大小 |
blockOffset |
檔案塊偏移(在整個檔案中的位置) |
blockSize |
檔案塊大小 |
blockIndex |
檔案塊索引(基於1) |
blockMd5 |
檔案塊MD5 |
complete |
當前檔案是否已經傳完 |
根據client_filename和last_modified_date,再加上系統中的其他關聯資訊,可以定位到本次上傳的檔案在服務端的大小,然後返回給客戶端。當然這是我自己的用法,你也可以根據自己的需求靈活設計。總之最終的目的就是要找到前臺選擇的檔案在伺服器上真正對應的檔案,並將已上傳大小正確返回。
另外需注意的一點,就是在續傳的第二步,不斷提交檔案片的過程中,也需要服務端準確定位到相應的檔案,不能把A的資料追加到B上。採用的方式也是提交fileName和lastModifyDate兩個引數(已寫在外掛內部,可服務端直接獲取),服務端找到對應的檔案進行追加。
另外再囉嗦一句,後臺獲取檔案的時候需要取成二進位制的,而我們提交是使用FormData來提交的,所以PHP程式碼需要這麼寫:
file_put_contents('uploads/'.$filename,file_get_contents($_FILES["file"]["tmp_name"]),FILE_APPEND);
如果上面的說明還是不夠清楚,就需要你自己來探索一下了,畢竟考慮到外掛可能應用在複雜的系統中,很多工作還是需要你來做的。或者你也可以給我留言,我很樂意為你解答疑惑。
該版本的其他改動
從1.0到2.0,Huploadify又新加了很多東西,不過只是新加,使用方式跟之前的沒有變化。例如上面的斷點續傳功能,你如果不想使用,只需設定breakPoints為false即可,外掛仍按照以前的方式工作。除了斷點續傳這個大頭,外掛還做了如下改動:
增加了onSelect回撥函式,在選擇了檔案之後觸發,用法與uploadify官網的一致
up6提供了3個事件,選擇檔案,選擇資料夾,貼上
使用者選擇檔案時會觸發open_files,選擇資料夾觸發open_folders,貼上會觸發以上兩個事件,因為使用者可能貼上的檔案和資料夾
刪除掉正在上傳的檔案,中斷髮送請求
完善了input file元件的accept屬性支援,瀏覽時只顯示執行的檔案格式,就是這個東東:
4.對外開放了方法呼叫介面,upload、stop、cancel、disable、ennable。我在demo中有演示。使用方法如下:var up = $('#upload').Huploadify({
auto:false,
fileTypeExts:'*.jpg;*.png;*.exe;*.mp3;*.mp4;*.zip;*.doc;*.docx;*.ppt;*.pptx;*.xls;*.xlsx;*.pdf',
multi:true
});
up.upload(1);//開始上傳檔案,接收一個引數,表示上傳第幾個檔案,可傳入*上傳佇列中的所有檔案
up.stop();//暫停上傳佇列中的所有檔案,不接收引數。用於開啟了斷點需傳
up.cancel(1);//刪除佇列中的某個檔案,接收一個引數,表示刪除第幾個檔案,可傳入*刪除佇列中的所有檔案
up.disable();//使選擇檔案按鈕失效,不接收引數
up.ennable();//使選擇檔案按鈕生效,不接收引數5. 修改其他已知bug
結束
外掛剛剛完成,與我們的後端程式設計師除錯完成了斷點續傳功能暫未發現問題,歡迎大家在使用的時候給我提任何問題。老實來講這個功能使用起來還是挺費解的,為了最大程度的保證靈活做成這樣,大家可以與我多多交流~
我在demo中使用了本地儲存來做已上傳檔案大小的儲存,下載壓縮包後可看一下效果。上傳一個比較大的視訊檔案,上傳到中間關閉瀏覽器,再次開啟瀏覽器上傳同一個檔案,會看到從上次斷掉的地方繼續上傳。
詳細內容可以參考我寫的這篇文章: