webuploader 實現 斷點續傳
webuploader是百度開發的上傳文件前端控件。可支持html5和flash,所以對瀏覽器的兼容比較好。由於須要用到ie8,ie8不支持html5,
因此必須支持flash上傳。
斷點續傳原理:
1)將大分件分片上傳,好比每次傳送3m。
2)後臺在上傳完畢後將分片上傳的文件合併爲一個文件。
技術要求:
1)前端頁面支持分件拆分讀取。html5是支持的。IE早期版本不能支持,能夠用flash來替代實現。
實現步驟:
1)頁面接受用戶傳入文件。
2)頁面用戶點擊「上傳」。
3)頁面將文件的基本信息發送到服務端,包括文件名稱,大小,修改時間 或者 文件md5(文件md5可加載三方md5算法,但在前端獲取須要花費大量時間),
要求不是很高能夠選第一種方案
4)服務端接受請求,根據md5生成目錄。
5)頁面將即將上傳的分片信息(不是分片文件)上傳到服務端請求驗證,判斷是否該分片是否已經上傳,已經上傳則該分片再也不重複上傳。
6)頁面分片傳送文件到服務端。
7)服務端將接受到的分片文件放置在md5目錄下。
8)頁面分片上傳完畢,發送合併請求。
9)服務端接受合併請求,將文件合併後放置到指定目錄,而後刪除臨時md5目錄。
10)完畢。
實現方法:
js調用webuploader上傳文件,配置爲分片上傳。
這樣能夠實現分片上傳,可是若是要實現斷點續傳(好比昨天上傳了一部分,關閉瀏覽器後,今天從新上傳的狀況),
還須要調用webupload提供的hook(WebUploader.Uploader.register)實現上傳前,上傳中,上傳完成後的事件觸發,發送到服務端請求。
代碼框架概述:
// uploader 初始化
var uploader = new WebUploader.Uploader({
// 選完文件後,是否自動上傳。
//auto: false,
//runtimeOrder: flash,html5, // 優先使用flash上傳
// swf文件路徑
swf: '/public/lib/webuploader/0.1.5/Uploader.swf',
//是否要分片處理大文件上傳。
chunked: true,
// 若是要分片,分多大一片? 默認大小爲5M.
chunkSize: chunkSize,
// 若是某個分片因爲網絡問題出錯,容許自動重傳多少次?
chunkRetry: 3,
// 上傳併發數。容許同時最大上傳進程數[默認值:3]
threads: 1,
// 去重
duplicate: true,
// 文件接收服務端。
server: server_url,
// 選擇文件的按鈕。可選。
// 內部根據當前運行是建立,多是input元素,也多是flash.
pick: {
id: filePicker,
multiple: false
},
// 不壓縮image, 默認若是是jpeg,文件上傳前會壓縮一把再上傳!
resize: false,
// 上傳本分片時預處理下一分片
prepareNextFile: true,
//formData: function(){return {uniqueFileName: '333'};}
formData: {uniqueFileName: uniqueFileName}
});
// 文件上傳過程當中建立進度條實時顯示。
uploader.on('uploadProgress', function (file, percentage) {
// 具體邏輯...
});
// 文件上傳成功處理。
uploader.on('uploadSuccess', function (file, response) {
// 具體邏輯...
});
// 文件上傳失敗處理。
uploader.on('uploadError', function (file) {
// 具體邏輯...
});
// 長傳完畢,無論成功失敗都會調用該事件,主要用於關閉進度條
uploader.on('uploadComplete', function (file) {
// 具體邏輯...
});
/** 實現webupload hook,觸發上傳前,中,後的調用關鍵 **/
WebUploader.Uploader.register({
"before-send-file": "beforeSendFile", // 整個文件上傳前
"before-send": "beforeSend", // 每一個分片上傳前
"after-send-file": "afterSendFile" // 分片上傳完畢
}, {
beforeSendFile: function (file) {
var task = new $.Deferred();
var start = new Date().getTime();
//拿到上傳文件的惟一名稱,用於斷點續傳
uniqueFileName = md5(file.name + file.size);
$.ajax({
type: "POST",
url: check_url, // 後臺url地址
data: {
type: "init",
uniqueFileName: uniqueFileName,
},
cache: false,
async: false, // 同步
timeout: 1000, //todo 超時的話,只能認爲該文件未曾上傳過
dataType: "json"
}).then(function (data, textStatus, jqXHR) {
if (data.complete) { //若存在,這返回失敗給WebUploader,代表該文件不須要上傳
task.reject();
// 業務邏輯...
} else {
task.resolve();
}
}, function (jqXHR, textStatus, errorThrown) { //任何形式的驗證失敗,都觸發從新上傳
task.resolve();
});
return $.when(task);
}
, beforeSend: function (block) {
//分片驗證是否已傳過,用於斷點續傳
var task = new $.Deferred();
$.ajax({
type: "POST",
url: check_url,
data: {
type: "block",
chunk: block.chunk,
size: block.end - block.start
},
cache: false,
async: false, // 同步
timeout: 1000, //todo 超時的話,只能認爲該分片未上傳過
dataType: "json"
}).then(function (data, textStatus, jqXHR) {
if (data.is_exists) { //若存在,返回失敗給WebUploader,代表該分塊不須要上傳
task.reject();
} else {
task.resolve();
}
}, function (jqXHR, textStatus, errorThrown) { //任何形式的驗證失敗,都觸發從新上傳
task.resolve();
});
return $.when(task);
}
, afterSendFile: function (file) {
var chunksTotal = Math.ceil(file.size / chunkSize);
if (chunksTotal > 1) {
//合併請求
var task = new $.Deferred();
$.ajax({
type: "POST",
url: check_url,
data: {
type: "merge",
name: file.name,
chunks: chunksTotal,
size: file.size
},
cache: false,
async: false, // 同步
dataType: "json"
}).then(function (data, textStatus, jqXHR) {
// 業務邏輯...
}, function (jqXHR, textStatus, errorThrown) {
current_uploader.uploader.trigger('uploadError');
task.reject();
});
return $.when(task);
}
}
});
舒適提示:
1. 前端用html5和flash上傳時上傳的文件修改事件的時區(美國時間和中國事件)可能不同,本身須要在後臺判斷處理,否則可能出現錯誤。
2. 若是同一個頁面有多個webuploader上傳,本身根據業務狀況特殊處理,由於WebUploader.Uploader.register是全局的,對每一個上傳都有影響。
html