我的需求:
我的需求可以簡(jiǎn)單描述為,對(duì)一個(gè)大文件進(jìn)行分片切割上傳。我實(shí)現(xiàn)的思路為,
對(duì)一個(gè)大文件,按照設(shè)定的chunksSize切分為N = file.size/chunkSize塊,
然后循環(huán)創(chuàng)建N個(gè)讀流讀取每個(gè)分片的內(nèi)容,然后發(fā)起N個(gè)http.request的Post請(qǐng)求去上傳文件。
代碼如下
(說(shuō)明: upload函數(shù)用來(lái)根據(jù)分塊的個(gè)數(shù)n,計(jì)算每塊起始標(biāo)志位和終止標(biāo)識(shí)位,并調(diào)用senddataPromise函數(shù)對(duì)每片進(jìn)行操作)
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; // 因讀取時(shí)包含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ù)是對(duì)第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請(qǐng)求發(fā)送分片數(shù)據(jù)
function doapost(data) {
return new Promise(function (resolve,reject) {
let i = data.n;
console.log("第"+i+"份請(qǐng)求準(zhǔn)備發(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+"份請(qǐng)求返回?cá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+"份請(qǐng)求已發(fā)出")
})
}
我的問(wèn)題:
按照正常的思路,因?yàn)樽x取文件內(nèi)容為異步操作,后面發(fā)送請(qǐng)求也為異步操作,所以
也就是說(shuō)會(huì)出現(xiàn)對(duì)于n個(gè)分片,讀取數(shù)據(jù)已經(jīng)讀取了p片,并且已經(jīng)有q(**q < p**)片
已經(jīng)完成上傳完成返回?cái)?shù)據(jù)的情況,但是現(xiàn)在問(wèn)題是,***發(fā)現(xiàn)并沒(méi)有分片上傳完返回?cái)?shù)據(jù)的
情況出現(xiàn),都是在n個(gè)分片讀取完成后,才開始統(tǒng)一執(zhí)行分片內(nèi)容上傳操作***
圖片如下:(由于圖片無(wú)法上傳,我把程序運(yùn)輸出拷貝一下)
{
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份請(qǐng)求準(zhǔn)備發(fā)出
第0份請(qǐng)求已發(fā)出
第1塊 JSON開始
第1塊 JSON結(jié)束
第1份請(qǐng)求準(zhǔn)備發(fā)出
第1份請(qǐng)求已發(fā)出
第2塊 JSON開始
第2塊 JSON結(jié)束
第2份請(qǐng)求準(zhǔn)備發(fā)出
第2份請(qǐng)求已發(fā)出
第3塊 JSON開始
第3塊 JSON結(jié)束
第3份請(qǐng)求準(zhǔn)備發(fā)出
第3份請(qǐng)求已發(fā)出
第5塊 JSON開始
第5塊 JSON結(jié)束
第5份請(qǐng)求準(zhǔn)備發(fā)出
第5份請(qǐng)求已發(fā)出
第4塊 JSON開始
第4塊 JSON結(jié)束
第4份請(qǐng)求準(zhǔn)備發(fā)出
第4份請(qǐng)求已發(fā)出
第6塊 JSON開始
第6塊 JSON結(jié)束
第6份請(qǐng)求準(zhǔn)備發(fā)出
第6份請(qǐng)求已發(fā)出
第8塊 JSON開始
第8塊 JSON結(jié)束
第8份請(qǐng)求準(zhǔn)備發(fā)出
第8份請(qǐng)求已發(fā)出
第7塊 JSON開始
第7塊 JSON結(jié)束
第7份請(qǐng)求準(zhǔn)備發(fā)出
第7份請(qǐng)求已發(fā)出
第8份請(qǐng)求返回?cái)?shù)據(jù)
moran999
第4份請(qǐng)求返回?cái)?shù)據(jù)
moran999
第6份請(qǐng)求返回?cái)?shù)據(jù)
moran999
第1份請(qǐng)求返回?cái)?shù)據(jù)
moran999
第2份請(qǐng)求返回?cái)?shù)據(jù)
moran999
第0份請(qǐng)求返回?cái)?shù)據(jù)
moran999
第3份請(qǐng)求返回?cái)?shù)據(jù)
moran999
第7份請(qǐng)求返回?cái)?shù)據(jù)
moran999
第5份請(qǐng)求返回?cái)?shù)據(jù)
moran999
[ 'end', 'end', 'end', 'end', 'end', 'end', 'end', 'end', 'end' ]
all done
可以看到其POST數(shù)據(jù)的發(fā)出并不是和讀流無(wú)關(guān)的,即任何一個(gè)POST都不會(huì)發(fā)出,
直到到所有的讀流讀取完數(shù)據(jù),想問(wèn)一下各位碼友是什么原因尼??因?yàn)檎? 理解下當(dāng)?shù)趇個(gè)讀流讀的時(shí)候,前面已經(jīng)讀取完內(nèi)容的讀流完全可以進(jìn)行post操作
了啊,但實(shí)際上并沒(méi)有。
之所以會(huì)問(wèn)這個(gè)問(wèn)題是因?yàn)楫?dāng)我輸入的文件比較大時(shí),他執(zhí)行到《第12塊 JSON開始時(shí),
就內(nèi)存溢出了》,而如果程序是post不用等待所有的讀流讀完時(shí),當(dāng)有一部分post執(zhí)行完之后,其對(duì)應(yīng)的數(shù)據(jù)就被回收了,釋放相應(yīng)的內(nèi)存,就不會(huì)出現(xiàn)內(nèi)存溢出了。
不是呀,這個(gè)是正常的呀,你看 第1份 post已經(jīng)發(fā)出
是在第2塊JSON開始
之前呀,post發(fā)出和 http.requset
并列,JSON
開始是讀完文件之后,所以發(fā)送數(shù)據(jù)是在 文件讀完之前,只是讀后面塊的時(shí)候前面的請(qǐng)求還沒(méi)有執(zhí)行完,所以并沒(méi)有 第n份數(shù)據(jù)返回
。 網(wǎng)絡(luò)延時(shí)比讀取文件大的多,所以文件讀完之前不會(huì)返回呀。
感覺(jué),這段代碼不至于在 12 的時(shí)候內(nèi)存溢出呀。
nodejs 上傳文件的話用 管道 更好一點(diǎn)兒吧。
如 @zonxin 所說(shuō),代碼和你設(shè)想的大致相同。
補(bǔ)充為什么第12塊就已經(jīng)溢出
let chunkjson = JSON.stringify(chunk);
把本來(lái)1M
的Buffer
轉(zhuǎn)成和數(shù)組樣式的字符串[104,101,...]
,內(nèi)存漲了x
倍(就不用說(shuō)后面還有個(gè)queryString.stringify
)。Buffer
除外),而樓主剛好把Buffer
轉(zhuǎn)成string
。pipe
(轉(zhuǎn)成string
也沒(méi)法用),以至于發(fā)送過(guò)的字節(jié)還保留在內(nèi)存,直到完整是字符串發(fā)送完,而完整的一塊缺有卻有x*1M
。北大青鳥APTECH成立于1999年。依托北京大學(xué)優(yōu)質(zhì)雄厚的教育資源和背景,秉承“教育改變生活”的發(fā)展理念,致力于培養(yǎng)中國(guó)IT技能型緊缺人才,是大數(shù)據(jù)專業(yè)的國(guó)家
北大青鳥中博軟件學(xué)院創(chuàng)立于2003年,作為華東區(qū)著名互聯(lián)網(wǎng)學(xué)院和江蘇省首批服務(wù)外包人才培訓(xùn)基地,中博成功培育了近30000名軟件工程師走向高薪崗位,合作企業(yè)超4
中公教育集團(tuán)創(chuàng)建于1999年,經(jīng)過(guò)二十年潛心發(fā)展,已由一家北大畢業(yè)生自主創(chuàng)業(yè)的信息技術(shù)與教育服務(wù)機(jī)構(gòu),發(fā)展為教育服務(wù)業(yè)的綜合性企業(yè)集團(tuán),成為集合面授教學(xué)培訓(xùn)、網(wǎng)
達(dá)內(nèi)教育集團(tuán)成立于2002年,是一家由留學(xué)海歸創(chuàng)辦的高端職業(yè)教育培訓(xùn)機(jī)構(gòu),是中國(guó)一站式人才培養(yǎng)平臺(tái)、一站式人才輸送平臺(tái)。2014年4月3日在美國(guó)成功上市,融資1
浪潮集團(tuán)項(xiàng)目經(jīng)理。精通Java與.NET 技術(shù), 熟練的跨平臺(tái)面向?qū)ο箝_發(fā)經(jīng)驗(yàn),技術(shù)功底深厚。 授課風(fēng)格 授課風(fēng)格清新自然、條理清晰、主次分明、重點(diǎn)難點(diǎn)突出、引人入勝。
曾工作于聯(lián)想擔(dān)任系統(tǒng)開發(fā)工程師,曾在博彥科技股份有限公司擔(dān)任項(xiàng)目經(jīng)理從事移動(dòng)互聯(lián)網(wǎng)管理及研發(fā)工作,曾創(chuàng)辦藍(lán)懿科技有限責(zé)任公司從事總經(jīng)理職務(wù)負(fù)責(zé)iOS教學(xué)及管理工作。
精通HTML5和CSS3;Javascript及主流js庫(kù),具有快速界面開發(fā)的能力,對(duì)瀏覽器兼容性、前端性能優(yōu)化等有深入理解。精通網(wǎng)頁(yè)制作和網(wǎng)頁(yè)游戲開發(fā)。
具有10 年的Java 企業(yè)應(yīng)用開發(fā)經(jīng)驗(yàn)。曾經(jīng)歷任德國(guó)Software AG 技術(shù)顧問(wèn),美國(guó)Dachieve 系統(tǒng)架構(gòu)師,美國(guó)AngelEngineers Inc. 系統(tǒng)架構(gòu)師。