摘要:所以,該文適合于全棧工程師,至少是想成為還有,為什么需要分片,不分片能實現(xiàn)斷點續(xù)傳嗎分片是為了充分利用網(wǎng)絡帶寬,加快上傳速度不分片也是能夠?qū)崿F(xiàn)斷點續(xù)傳的。詳細參考文件上傳組件深度剖析分片上傳與斷點續(xù)傳之間沒有很直接的關系好了,進入正題。
上傳文件,基本上是每一個網(wǎng)站應用都會具備的一個功能。對于一個網(wǎng)絡存儲應用,對于上傳功能要求更是迫切。
如今市面上成熟上傳插件,如WebUploader,"體積太大",不適合于移動端上傳;再加上作為一位程序員的"操守",當然還是更喜歡自己造輪子。
于是花了一天半時間,MoUploader應運而生。為什么叫MoUploader呢?Mo表示Mobile(其實更是因為我的綽號moyu)
關于實現(xiàn)原理首先需要明確,上傳這東西不僅僅是只需要前端就能完成的很好的,需要前端后端統(tǒng)一數(shù)據(jù)格式,從而實現(xiàn)斷點續(xù)傳。(所以,該文適合于全棧工程師,至少是想成為)
還有,為什么需要分片,不分片能實現(xiàn)斷點續(xù)傳嗎?分片是為了充分利用網(wǎng)絡帶寬,加快上傳速度;不分片也是能夠?qū)崿F(xiàn)斷點續(xù)傳的。詳細參考 HTML5文件上傳組件深度剖析.
分片上傳與斷點續(xù)傳之間沒有很直接的關系.
好了,進入正題。
實現(xiàn)斷點續(xù)傳的前提是需要服務器記錄某文件的上傳進度,那么根據(jù)什么判斷是不是同一個文件呢?可以利用文件內(nèi)容求md5碼,如果文件過大,求取md5碼也是一個很長的過程,所以對于大文件,只能針對某一段數(shù)據(jù)進行計算,加上服務器對cookie用戶信息的判斷,得到相對唯一的key。
在前端頁面,需要將文件按照一定大小進行分片,一次請求只發(fā)送這一小片數(shù)據(jù),所以我們可以同時發(fā)起多個請求。但一次同時請求的連接數(shù)不宜過多,服務器負載過重。對于文件分片操作,H5具有十分強大的File API,直接利用File對象的slice方法即可得到Blob對象。
至于同時傳輸數(shù)據(jù)的連接數(shù)控制邏輯,就需要花點腦子思考了。前端把數(shù)據(jù)順利得傳給服務器了,服務器只需要按照數(shù)據(jù)中給的開始字節(jié)位置,與讀取到的文件片段數(shù)據(jù),寫入文件即可
更多信息就看源碼吧!MoUploader
功能實現(xiàn)文件結構
file-upload/ ├── bower_components/ # bower包 ├── db.js # 數(shù)據(jù)操作接口 ├── demo.html ├── md5.json # 數(shù)據(jù) ├── mouploader.js # 源碼 ├── README.md └── server.js # demo.html服務, 建立在3000端口 1 directories, 8 files.
(打印文件目錄樹使用的是自己寫的print-dir)
怎么使用
引入script,amd/cmd/...,
使用MoUploader
input.onchange = function (e) { var self = this; var moUploader = MoUploader({ files: this.files, uploadUrl: "/upload", request: false, onBeforeUpload: function (index) { if(index>=0) { self.files[index].progress = appendUploading(self.files[index], index) } }, onOverAllProgress: function (index, loaded, total) { console.log(loaded / total) //setProgress(loaded / total, self.files[index].progress) }, onLoad: function (index, chunkIndex, chunksNum) { console.log("onLoad", this, arguments) }, onAbort: function (index, chunkIndex, chunksNum) { console.log("onAbort", this, arguments) }, onError: function (index, chunkIndex, chunksNum) { console.log("onError", this, arguments) }, onContinue: function (file, md5, index) { return new Promise(function(reslove, reject) { var xhr = new XMLHttpRequest() xhr.open("GET", "/getFile?md5="+md5, true); xhr.send(null); xhr.addEventListener("readystatechange", function () { if(xhr.readyState === 4 && xhr.status === 200) { var json = JSON.parse(xhr.responseText); log(json) reslove(json.pos) } }) }) } }) // pause or continue upload // if index < 0, will run for all files // moUploader.pause(index); // moUploader.continue(index); }
配置選項
var default_ops = { // chunk Size: byte chunkSize: (1<<20) * 5, // Number: request Number. // Array: files requests. // Boolean: open or close Slice, if false, chunkSize don"t work. request: 3, files: [], uploadUrl: "/", // function: get uploaded pos. // arguments: file, md5, index. // need return a promise object which will return uploaded pos. onContinue: null, // if false, md5 will be setted by filename. md5: true, // md5Size: slice file 0 - md5Size for calculate md5 md5Size: (1<<20) * 50, // called when before upload. // arguments: file index or -1 (will begin upload) onBeforeUpload: null, // function: uploading progress listener. // *only listen one request.* // arguments: index, chunkIndex, chunksNum, loaded, total. onProgress: null, // function: overall uploading progress listener. // arguments: index, loaded, total onOverAllProgress: null, // function: called when one request is ended. // arguments: index, chunkIndex, chunksNum onLoad: null, // function: called when one request is aborted. // arguments: index, chunkIndex, chunksNum onAbort: null, // function: called when one request happens error. // arguments: index, chunkIndex, chunksNum onError: null }
服務器數(shù)據(jù)處理 (Node.js)
數(shù)據(jù)分段寫入文件
function writeBuffer(bf, path, pos) { var fd = fs.openSync(path, "a+"); fs.writeSync(fd, bf, 0, bf.length, Number(pos) || 0) console.log(`write buffer, pos: ${pos}, path: ${path}, length: ${bf.length}`) } function store(param, chunks) { param.chunks = param.chunks || 1 param.chunk = param.chunk || 0 var p = path.join("./upload", param.name) var bf = Buffer.concat(chunks); var json = db.get(param.md5); if(json) { json.pos = parseInt(json.pos!=null?json.pos : 0) json.size = parseInt(json.size!=null?json.size : 0) } if(!json || (json.pos+json.size) <= param.pos) { // 新的數(shù)據(jù)pos比數(shù)據(jù)庫中大,更新數(shù)據(jù) param.size = bf.length db.set(param.md5, param) db.save(); writeBuffer(bf, p, param.pos || 0) } } var multiparty = require("multiparty") var form = new multiparty.Form({ autoFields: true, autoFiles: false, }); form.on("part", (part) => { form.on("aborted", () => { //意外退出或者暫停都會保存數(shù)據(jù) console.log("aborted"); store(param, chunks) }) var chunks = [] part.on("data", (data) => { if(part.filename) { chunks.push(data) } }).on("end", () => { console.log("end") store(param, chunks) }) }); form.on("field", (name, value) => { param[name] = value; });
文章版權歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/87909.html
摘要:基于的大文件分片上傳我們在做文件上傳的時候,如果文件過大,可能會導致請求超時的情況。所以,在遇到需要對大文件進行上傳的時候,就需要對文件進行分片上傳的操作。所以把文件名稱加上。后續(xù)延伸斷點續(xù)傳多文件多批次上傳 基于Node.js的大文件分片上傳 我們在做文件上傳的時候,如果文件過大,可能會導致請求超時的情況。所以,在遇到需要對大文件進行上傳的時候,就需要對文件進行分片上傳的操作。同時如...
摘要:基于的大文件分片上傳我們在做文件上傳的時候,如果文件過大,可能會導致請求超時的情況。所以,在遇到需要對大文件進行上傳的時候,就需要對文件進行分片上傳的操作。所以把文件名稱加上。后續(xù)延伸斷點續(xù)傳多文件多批次上傳 基于Node.js的大文件分片上傳 我們在做文件上傳的時候,如果文件過大,可能會導致請求超時的情況。所以,在遇到需要對大文件進行上傳的時候,就需要對文件進行分片上傳的操作。同時如...
閱讀 1339·2021-11-25 09:43
閱讀 751·2021-11-18 10:02
閱讀 2877·2021-09-07 09:59
閱讀 2757·2021-08-30 09:44
閱讀 2928·2019-08-30 13:17
閱讀 2317·2019-08-29 12:17
閱讀 1681·2019-08-28 17:57
閱讀 1290·2019-08-26 14:04