我的需求:
我的需求可以簡單描述為,對一個大文件進行分片切割上傳。我實現(xiàn)的思路為,
對一個大文件,按照設(shè)定的chunksSize切分為N = file.size/chunkSize塊,
然后循環(huán)創(chuàng)建N個讀流讀取每個分片的內(nèi)容,然后發(fā)起N個http.request的Post請求去上傳文件。
代碼如下
(說明: upload函數(shù)用來根據(jù)分塊的個數(shù)n,計算每塊起始標志位和終止標識位,并調(diào)用senddataPromise函數(shù)對每片進行操作)
function upload(username,filepath,file_id,filelength,n,alreadychunks,chunkSize) {
return new Promise(function (resolve,reject) {
var start = 0,end = 0;
var promiseall = [];
for (let curindex = 0;curindex < n;curindex++) {
if(filelength - start <= chunkSize) {
end = filelength - 1;
}else {
end = start+chunkSize - 1; // 因讀取時包含start和end位
}
if(alreadychunks.indexOf(curindex) == -1) {
let options = {
flags: 'r',
highWaterMark: chunkSize,
start: start,
end: end
};
promiseall.push(senddataPromise(filepath,options,username,file_id,curindex,end-start+1));
}
start = end + 1;
}
let timer = setInterval(() => {
if(promiseall.length == n) {
clearInterval(timer);
Promise.all(promiseall).then(values=>{
console.log(values);
console.log("all done");
resolve(true)
}).catch(err => {
console.log(err);
reject(err);
})
}
},500)
})
}
senddataPromise函數(shù)是對第i塊分片創(chuàng)建讀流讀取內(nèi)容,并調(diào)用doapost函數(shù)發(fā)送到后端
function senddataPromise(path,options,username,summary,curindex,length) {
return new Promise(function (resolve,reject) {
let readSteam = fs.createReadStream(path,options);
readSteam.on("data",(chunk) => {
console.log("第"+curindex+"塊 JSON開始")
let chunkjson = JSON.stringify(chunk);
console.log("第"+curindex+"塊 JSON結(jié)束")
let tempcell = {
data: chunkjson,
n: curindex,
file_id: summary,
username: username,
length: length
};
chunk = null;
chunkjson = null;
doapost(tempcell).then(values=>{
resolve(values)
}).catch(err=>{
reject(err);
});
})
})
}
doapost函數(shù)發(fā)起post請求發(fā)送分片數(shù)據(jù)
function doapost(data) {
return new Promise(function (resolve,reject) {
let i = data.n;
console.log("第"+i+"份請求準備發(fā)出")
let contents = queryString.stringify(data);
data = null;
let options = {
host: "localhost",
path: "/nodepost/",
port: 8000,
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': contents.length
}
};
let req = http.request(options, function (res) {
console.log("第"+i+"份請求返回數(shù)據(jù)")
res.on("data", function (chunk) {
console.log(chunk.toString());
});
res.on("end", function (d) {
resolve("end");
});
res.on("error", function (e) {
reject(e);
})
});
req.write(contents);
req.end();
contents = null;
console.log("第"+i+"份請求已發(fā)出")
})
}
我的問題:
按照正常的思路,因為讀取文件內(nèi)容為異步操作,后面發(fā)送請求也為異步操作,所以
也就是說會出現(xiàn)對于n個分片,讀取數(shù)據(jù)已經(jīng)讀取了p片,并且已經(jīng)有q(**q < p**)片
已經(jīng)完成上傳完成返回數(shù)據(jù)的情況,但是現(xiàn)在問題是,***發(fā)現(xiàn)并沒有分片上傳完返回數(shù)據(jù)的
情況出現(xiàn),都是在n個分片讀取完成后,才開始統(tǒng)一執(zhí)行分片內(nèi)容上傳操作***
圖片如下:(由于圖片無法上傳,我把程序運輸出拷貝一下)
{
kind: 'upload',
username: 'moran999',
filepath: 'F:/my_upload_test/NowTest.pdf',
file_id: '-196987878-472217752177633040957425519',
alreadychunks: [],
chunkSize: 1048576,
n: 9 }
第0塊 JSON開始
第0塊 JSON結(jié)束
第0份請求準備發(fā)出
第0份請求已發(fā)出
第1塊 JSON開始
第1塊 JSON結(jié)束
第1份請求準備發(fā)出
第1份請求已發(fā)出
第2塊 JSON開始
第2塊 JSON結(jié)束
第2份請求準備發(fā)出
第2份請求已發(fā)出
第3塊 JSON開始
第3塊 JSON結(jié)束
第3份請求準備發(fā)出
第3份請求已發(fā)出
第5塊 JSON開始
第5塊 JSON結(jié)束
第5份請求準備發(fā)出
第5份請求已發(fā)出
第4塊 JSON開始
第4塊 JSON結(jié)束
第4份請求準備發(fā)出
第4份請求已發(fā)出
第6塊 JSON開始
第6塊 JSON結(jié)束
第6份請求準備發(fā)出
第6份請求已發(fā)出
第8塊 JSON開始
第8塊 JSON結(jié)束
第8份請求準備發(fā)出
第8份請求已發(fā)出
第7塊 JSON開始
第7塊 JSON結(jié)束
第7份請求準備發(fā)出
第7份請求已發(fā)出
第8份請求返回數(shù)據(jù)
moran999
第4份請求返回數(shù)據(jù)
moran999
第6份請求返回數(shù)據(jù)
moran999
第1份請求返回數(shù)據(jù)
moran999
第2份請求返回數(shù)據(jù)
moran999
第0份請求返回數(shù)據(jù)
moran999
第3份請求返回數(shù)據(jù)
moran999
第7份請求返回數(shù)據(jù)
moran999
第5份請求返回數(shù)據(jù)
moran999
[ 'end', 'end', 'end', 'end', 'end', 'end', 'end', 'end', 'end' ]
all done
可以看到其POST數(shù)據(jù)的發(fā)出并不是和讀流無關(guān)的,即任何一個POST都不會發(fā)出,
直到到所有的讀流讀取完數(shù)據(jù),想問一下各位碼友是什么原因尼??因為正常
理解下當(dāng)?shù)趇個讀流讀的時候,前面已經(jīng)讀取完內(nèi)容的讀流完全可以進行post操作
了啊,但實際上并沒有。
之所以會問這個問題是因為當(dāng)我輸入的文件比較大時,他執(zhí)行到《第12塊 JSON開始時,
就內(nèi)存溢出了》,而如果程序是post不用等待所有的讀流讀完時,當(dāng)有一部分post執(zhí)行完之后,其對應(yīng)的數(shù)據(jù)就被回收了,釋放相應(yīng)的內(nèi)存,就不會出現(xiàn)內(nèi)存溢出了。
如 @zonxin 所說,代碼和你設(shè)想的大致相同。
補充為什么第12塊就已經(jīng)溢出
let chunkjson = JSON.stringify(chunk);把本來1M的Buffer轉(zhuǎn)成和數(shù)組樣式的字符串[104,101,...],內(nèi)存漲了x倍(就不用說后面還有個queryString.stringify)。Buffer除外),而樓主剛好把Buffer轉(zhuǎn)成string。pipe(轉(zhuǎn)成string也沒法用),以至于發(fā)送過的字節(jié)還保留在內(nèi)存,直到完整是字符串發(fā)送完,而完整的一塊缺有卻有x*1M。北大青鳥APTECH成立于1999年。依托北京大學(xué)優(yōu)質(zhì)雄厚的教育資源和背景,秉承“教育改變生活”的發(fā)展理念,致力于培養(yǎng)中國IT技能型緊缺人才,是大數(shù)據(jù)專業(yè)的國家
達內(nèi)教育集團成立于2002年,是一家由留學(xué)海歸創(chuàng)辦的高端職業(yè)教育培訓(xùn)機構(gòu),是中國一站式人才培養(yǎng)平臺、一站式人才輸送平臺。2014年4月3日在美國成功上市,融資1
北大課工場是北京大學(xué)校辦產(chǎn)業(yè)為響應(yīng)國家深化產(chǎn)教融合/校企合作的政策,積極推進“中國制造2025”,實現(xiàn)中華民族偉大復(fù)興的升級產(chǎn)業(yè)鏈。利用北京大學(xué)優(yōu)質(zhì)教育資源及背
博為峰,中國職業(yè)人才培訓(xùn)領(lǐng)域的先行者
曾工作于聯(lián)想擔(dān)任系統(tǒng)開發(fā)工程師,曾在博彥科技股份有限公司擔(dān)任項目經(jīng)理從事移動互聯(lián)網(wǎng)管理及研發(fā)工作,曾創(chuàng)辦藍懿科技有限責(zé)任公司從事總經(jīng)理職務(wù)負責(zé)iOS教學(xué)及管理工作。
浪潮集團項目經(jīng)理。精通Java與.NET 技術(shù), 熟練的跨平臺面向?qū)ο箝_發(fā)經(jīng)驗,技術(shù)功底深厚。 授課風(fēng)格 授課風(fēng)格清新自然、條理清晰、主次分明、重點難點突出、引人入勝。
精通HTML5和CSS3;Javascript及主流js庫,具有快速界面開發(fā)的能力,對瀏覽器兼容性、前端性能優(yōu)化等有深入理解。精通網(wǎng)頁制作和網(wǎng)頁游戲開發(fā)。
具有10 年的Java 企業(yè)應(yīng)用開發(fā)經(jīng)驗。曾經(jīng)歷任德國Software AG 技術(shù)顧問,美國Dachieve 系統(tǒng)架構(gòu)師,美國AngelEngineers Inc. 系統(tǒng)架構(gòu)師。