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是全局的,對每個上傳都有影響。
