常見異步上傳方案
-
使用第三方控件(Flash瘦赫,ActiveX, 瀏覽器插件等)
優(yōu)點:
- 交互與可控性好(多文件、進(jìn)度展示蛤迎、續(xù)傳确虱、暫停)
- 性能好(可使用底層協(xié)議通信)
缺點:
+ 需要瀏覽器安裝插件
-
使用隱藏的iframe模擬異步上傳
優(yōu)點:
- 瀏覽器原生支持,不需要插件
- 廣泛的瀏覽器兼容性
缺點:
- 交互差替裆,體驗差校辩,上傳過程基本不可控
- 性能差
關(guān)鍵技術(shù)點:
- form指定target,提交結(jié)果定向到隱藏的iframe辆童。
- 提交完成后宜咒,iframe中頁面與主頁面通信,通知上傳結(jié)果及服務(wù)端文件信息把鉴。
html
<form action="/upload2" enctype="multipart/form-data" method="post" target="frm" onsubmit="loading(true);">
<p id="upfile">
附件: <input type="file" name="myfile" style="display: inline">
</p>
<p id="upbtn">
<input class="btn btn-primary btn-sm" style="padding-left:50px;padding-right: 50px;" type="submit" value="異步上傳">
<span id="uptxt" style="display: none">正在上傳...</span>
</p>
</form>
<div id="flist" style="border:1px dotted darkgray;"></div>
script
// 上傳完成后的回調(diào)
function uploadFinished(fileName) {
addToFlist(fileName);
loading(false);
}
function addToFlist(fname) {
var temp = ["<p id='" + fname + "'>",
fname,
"<button onclick='delFile(\"" + fname + "\");'>刪除</button>",
"</p>"
];
$("#flist").append(temp.join(""));
}
function delFile(fname) {
console.log('to delete file: ' + fname);
// TODO: 請實現(xiàn)
}
function loading(showloading) {
if (showloading) {
$("#uptxt").show();
} else {
$("#uptxt").hide();
}
}
node.js
var express = require('express');
var router = express.Router();
var multipart = require('connect-multiparty');
var multipartMiddleware = multipart();
var fs = require('fs');
router.all('/', function (req, res) {
res.sendFile('../public/index.html');
});
router.post('/upload2', multipartMiddleware, function(req, res) {
console.log(req.body);
console.log(req.files);
// 實際編程時故黑,一般要將臨時文件移動到目標(biāo)位置,之后刪除臨時文件
// 課程中為簡化操作庭砍,直接將臨時文件當(dāng)成目標(biāo)文件
var fpath = req.files.myfile.path;
var fname = fpath.substr(fpath.lastIndexOf('\\') + 1);
setTimeout(function() {
var ret = ["<script>",
"window.parent.uploadFinished('" + fname + "');",
"</script>"];
res.send(ret.join(""));
}, 3000);
});
module.exports = router;
-
使用xhr level 2 純ajax異步上傳
優(yōu)點:
- 支持H5的瀏覽器原生支持场晶,不需要插件
- 交互性較好
缺點:
- 受瀏覽器支持限制
關(guān)鍵過程:
創(chuàng)建FormData,放入待上傳文件
通過xhr操作將FormData發(fā)送到服務(wù)器怠缸,實現(xiàn)文件上傳
綁定progress峰搪、load、error等事件監(jiān)聽傳輸過程并在頁面顯示動態(tài)交互信息
html
<div>
<p id="upfile">附件: <input type="file" id="myfile" style="display: inline"></p>
<p id="upbtn">
<input class="btn btn-primary btn-sm" style="padding-left:50px;padding-right: 50px;" type="button" value="異步上傳" onclick="upload();">
<span id="uptxt" style="display: none">正在上傳...</span>
<span id="upprog"></span>
<button id="stopbtn" style="display:none;">停止上傳</button>
</p>
</div>
<div id="flist" style="border:1px dotted darkgray;"></div>
javascript
function upload() {
// 1.準(zhǔn)備FormData
var fd = new FormData();
fd.append("myfile", $("#myfile")[0].files[0]);
// 創(chuàng)建xhr對象
var xhr = new XMLHttpRequest();
// 監(jiān)聽狀態(tài)凯旭,實時響應(yīng)
// xhr 和 xhr.upload 都有progress事件概耻,xhr.progress是下載進(jìn)度使套,xhr.upload.progress是上傳進(jìn)度
xhr.upload.onprogress = function(event) {
if (event.lengthComputable) {
var percent = Math.round(event.loaded * 100 / event.total);
console.log('%d%', percent);
$("#upprog").text(percent);
}
};
// 傳輸開始事件
xhr.onloadstart = function(event) {
console.log('load start');
$("#upprog").text('開始上傳');
$("#stopbtn").one('click', function() {
xhr.abort();
$(this).hide();
});
loading(true);
};
// ajax過程成功完成事件
xhr.onload = function(event) {
console.log('load success');
$("#upprog").text('上傳成功');
console.log(xhr.responseText);
var ret = JSON.parse(xhr.responseText);
addToFlist(ret.fname);
};
// ajax過程發(fā)生錯誤事件
xhr.onerror = function(event) {
console.log('error');
$("#upprog").text('發(fā)生錯誤');
};
// ajax被取消
xhr.onabort = function(event) {
console.log('abort');
$("#upprog").text('操作被取消');
};
// loadend傳輸結(jié)束,不管成功失敗都會被觸發(fā)
xhr.onloadend = function (event) {
console.log('load end');
loading(false);
};
// 發(fā)起ajax請求傳送數(shù)據(jù)
xhr.open('POST', '/upload3', true);
xhr.send(fd);
}
function addToFlist(fname) {
var temp = ["<p id='" + fname + "'>",
fname,
"<button onclick='delFile(\"" + fname + "\");'>刪除</button>",
"</p>"
];
$("#flist").append(temp.join(""));
}
function delFile(fname) {
console.log('to delete file: ' + fname);
// TODO: 請實現(xiàn)
}
function loading(showloading) {
if (showloading) {
$("#uptxt").show();
$("#stopbtn").show();
} else {
$("#uptxt").hide();
$("#stopbtn").hide();
}
}
node.js
var express = require('express');
var router = express.Router();
var multipart = require('connect-multiparty');
var multipartMiddleware = multipart();
var fs = require('fs');
router.all('/', function (req, res) {
res.sendFile('../public/index.html');
});
router.post('/upload3', multipartMiddleware, function(req, res) {
console.log(req.body);
console.log(req.files);
// 實際編程時鞠柄,一般要將臨時文件移動到目標(biāo)位置侦高,之后刪除臨時文件
// 課程中為簡化操作,直接將臨時文件當(dāng)成目標(biāo)文件
var fpath = req.files.myfile.path;
var fname = fpath.substr(fpath.lastIndexOf('\\') + 1);
res.json({fname: fname});
});
module.exports = router;