深入解析Nodejs中的大文件讀寫
前言
最近在做一些node端的文件讀寫和分片上傳工作,在這個(gè)過程中,發(fā)現(xiàn)node讀取的文件如果超過2G,超過了讀取Blob最大值,會(huì)出現(xiàn)讀取異常,此外在node中讀寫文件也受服務(wù)器RAM的限制等,需要分片讀取,本人記錄一下遇到的問題以及解決問題的經(jīng)過。
- node中的文件讀寫
- node文件讀寫RAM和Blob大小的限制
- 其他
一、node中的文件讀寫
1.1 常規(guī)文件讀寫
常規(guī)的,如果我們要讀取一個(gè)比較小的文件,可以直接通過:
const fs = require('fs')
let data = fs.readFileSync("./test.png")
console.log(data,123)
//輸出data = <Buffer 89 50 4e ...>一般而言,同步的方法不是很推薦,因?yàn)閖s/nodejs是單線程的,同步的方法會(huì)阻塞主線程。最新版的node直接提供了fs.promise,可以結(jié)合async/await直接使用:
const fs = require('fs')
const readFileSync = async () => {
let data = await fs.promises.readFile("./test.png")
console.log(data,123)
}
readFileSync()
//輸出data = <Buffer 89 50 4e ...>這里通過異步的方法調(diào)用不會(huì)阻塞主線程,多個(gè)文件讀取的IO也可以并行進(jìn)行等。
1.2 Stream文件讀寫
常規(guī)的文件讀寫,我們會(huì)把文件一次性的讀取到內(nèi)存中,這種方法時(shí)間效率和內(nèi)存效率都很低,時(shí)間效率低是指必須要一次性讀取完畢后才能執(zhí)行后續(xù)才做,內(nèi)存效率低是指必須把這個(gè)文件都一次性讀取放入內(nèi)存中,很占用內(nèi)存。
因此這種情況下,我們一般使用Stream來進(jìn)行文件的讀取:
const fs = require('fs')
const readFileTest = () => {
var data = ''
var rs = fs.createReadStream('./test.png');
rs.on('data', function(chunk) {
data += chunk;
console.log(chunk)
});
rs.on('end',function(){
console.log(data);
});
rs.on('error', function(err){
console.log(err.stack);
});
}
readFileTest()
// data = <Buffer 89 50 64 ...>通過Steam來進(jìn)行文件讀寫,可以提高內(nèi)存效率和時(shí)間效率。
- 內(nèi)存效率:在處理數(shù)據(jù)之前,不需要在內(nèi)存中加載大量(或整個(gè))數(shù)據(jù)
- 時(shí)間效率:一旦有了數(shù)據(jù),就可以開始處理,這大大減少開始處理數(shù)據(jù)的時(shí)間,而不必等到整個(gè)數(shù)據(jù)加載完畢再進(jìn)行處理。
Stream的文件還支持第二種寫法:
const fs = require('fs')
const readFileTest = () => {
var data = ''
var chunk;
var rs = fs.createReadStream('./test.png');
rs.on('readable', function() {
while ((chunk=rs.read()) != null) {
data += chunk;
}});
rs.on('end', function() {
console.log(data)
});
};
readFileTest()二、node文件讀寫RAM和Blob大小的限制
2.1 基礎(chǔ)問題
在讀取大文件時(shí),會(huì)有讀取文件大小的限制,比如我們現(xiàn)在在讀取一個(gè)2.5G的視頻文件:
const fs = require('fs')
const readFileTest = async () => {
let data = await fs.promises.readFile("./video.mp4")
console.log(data)
}
readFileTest()執(zhí)行上述的代碼會(huì)報(bào)錯(cuò):
RangeError [ERR_FS_FILE_TOO_LARGE]: File size (2246121911) is greater than 2 GB
我們可能會(huì)想到,通過設(shè)置option,NODE_OPTIONS='--max-old-space-size=5000',此時(shí)5000M>2.5G,但是報(bào)錯(cuò)還是沒有消失,也就是說通過Options無法改變node讀取文件的大小限制。
上述是常規(guī)的方式讀取大文件,如果通過Steam的方式讀取還會(huì)有文件大小的限制嘛? 比如:
const fs = require('fs')
const readFileTest = () => {
var data = ''
var rs = fs.createReadStream('./video.mp4');
rs.on('data', function(chunk) {
data += chunk;
});
rs.on('end',function(){
console.log(data);
});
rs.on('error', function(err){
console.log(err.stack);
});
}
readFileTest()如上方式讀取一個(gè)2.5G的文件不會(huì)有異常,不過要注意的是這邊有一個(gè)報(bào)錯(cuò):
data += chunk;
^
RangeError: Invalid string length
此時(shí)是因?yàn)閐ata的長度超過了最大限制,比如2048M等。因此在用Steam處理的時(shí)候,在對(duì)讀取結(jié)果的保存時(shí),要注意文件的大小,千萬不能超過默認(rèn)的Buffer的最大值。上述這種情況,我們不用data += chunk將數(shù)據(jù)全部保存在一個(gè)大的data中,我們可以邊讀取邊處理。
2.2 分片讀取
createReadStream在讀取文件的過程中,其實(shí)也可以分段讀取,這種分段讀取的方法也可以做為大文件讀取的備選項(xiàng)。特別是在并發(fā)讀取的時(shí)候有一定的優(yōu)點(diǎn),可以提升文件讀取和處理的速度。
createReadStream接受第二個(gè)參數(shù){start,end}。我們可以通過fs.promises.stat來獲取文件的大小,然后確定分片,最后分片一次讀取,比如:
- 獲取文件大小
const info = await fs.promises.stat(filepath) const size = info.size
- 按照指定的SIZE分片(比如128M一個(gè)分片)
const SIZE = 128 * 1024 * 1024
let sizeLen = Math.floor(size/SIZE)
let total = sizeLen +1 ;
for(let i=0;i<=sizeLen;i++){
if(sizeLen ===i){
console.log(i*SIZE,size,total,123)
readStremfunc(i*SIZE,size,total)
}else{
console.log(i*SIZE,(i+1)*SIZE,total,456)
readStremfunc(i*SIZE,(i+1)*SIZE-1,total)
}
}
//分片后【0,128M】,【128M, 256M】...3.實(shí)現(xiàn)讀取函數(shù)
const readStremfunc = () => {
const readStream = fs.createReadStream(filepath,{start:start,end:end})
readStream.setEncoding('binary')
let data = ''
readStream.on('data', chunk => {
data = data + chunk
})
readStream.end('data', () => {
...
})
}值得注意的是fs.createReadStream(filepath,{start,end}),start和end是前閉后閉的,比如fs.createReadSteam(filepath,{start:0,end:1023})讀取的是[0,1023]一共1024個(gè)bit。
三、其他
3.1 擴(kuò)展瀏覽器端的大文件讀寫、
前面將了大文件在nodejs中的讀取,那么在瀏覽器端會(huì)讀取大文件會(huì)有什么問題嗎?
瀏覽器在本地讀取大文件時(shí),之前有類似FileSaver、StreamSaver等方案,不過在瀏覽器本身添加了File的規(guī)范,使得瀏覽器本身就默認(rèn)和優(yōu)化了Stream的讀取。我們不需要做額外的工作,不過不同的版本會(huì)有兼容性的問題,我們還是可以通過FileSaver等進(jìn)行兼容。
3.2 請(qǐng)求靜態(tài)資源大文件
如果是在瀏覽器中獲取靜態(tài)資源大文件,一般情況下只需要通過range分配請(qǐng)求即可,一般的CDN加速域名,不管是阿里云還是騰訊云,對(duì)于分片請(qǐng)求都支持的很好,我們可以將資源通過cdn加速,然后在瀏覽器端直接請(qǐng)求cdn加速有的資源。
分片獲取cdn靜態(tài)資源大文件的步驟為,首先通過head請(qǐng)求獲取文件大?。?/strong>
const getHeaderInfo = async (url: string) => {
const res: any = await axios.head(url + `?${Math.random()}`);
return res?.headers;
};
const header = getHeaderInfo(source_url)
const size = header['content-length']我們可以從header中的content-length屬性中,獲取文件的大小。
然后進(jìn)行分片和分段,最后發(fā)起range請(qǐng)求:
const getRangeInfo = async (url: string, start: number, end: number) => {
const data = await axios({
method: 'get',
url,
headers: {
range: `bytes=${start}-${end}`,
},
responseType: 'blob',
});
return data?.data;
};在headers中指定 range: bytes=${start}-${end},就可以發(fā)起分片請(qǐng)求去獲取分段資源,這里的start和end也是前閉后閉的。
到此這篇關(guān)于深入解析Nodejs中的大文件讀寫的文章就介紹到這了,更多相關(guān)Nodejs大文件讀寫內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解nodejs微信公眾號(hào)開發(fā)——1.接入微信公眾號(hào)
本篇文章主要介紹了詳解nodejs微信公眾號(hào)開發(fā)——1.接入微信公眾號(hào),非常具有實(shí)用價(jià)值,需要的朋友可以參考下2017-04-04
使用Node.js創(chuàng)建本地HTTP服務(wù)器并實(shí)現(xiàn)異地遠(yuǎn)程訪問的方法
Node.js 是能夠在服務(wù)器端運(yùn)行 JavaScript 的開放源代碼、跨平臺(tái)運(yùn)行環(huán)境,這篇文章主要介紹了如何使用Node.js快速創(chuàng)建本地HTTP服務(wù)器并實(shí)現(xiàn)異地遠(yuǎn)程訪問,需要的朋友可以參考下2024-01-01
node.js中的fs.createWriteStream方法使用說明
這篇文章主要介紹了node.js中的fs.createWriteStream方法使用說明,本文介紹了fs.createWriteStream方法說明、語法、接收參數(shù)、使用實(shí)例和實(shí)現(xiàn)源碼,需要的朋友可以參考下2014-12-12
NodeJS使用formidable實(shí)現(xiàn)文件上傳
這篇文章主要為大家詳細(xì)介紹了NodeJS使用formidable實(shí)現(xiàn)文件上傳的相關(guān)方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-10-10
Node.js查詢MySQL并返回結(jié)果集給客戶端的全過程
nodejs最大的優(yōu)勢(shì)也是大家用著最為難以理解的一點(diǎn),就是它的異步功能,它幾乎所有的io操作都是異步的,這也就導(dǎo)致很多人不理解也用不習(xí)慣,下面這篇文章主要給大家介紹了關(guān)于Node.js查詢MySQL并返回結(jié)果集給客戶端的相關(guān)資料,需要的朋友可以參考下2022-12-12
Node.js net模塊功能及事件監(jiān)聽用法分析
這篇文章主要介紹了Node.js net模塊功能及事件監(jiān)聽用法,結(jié)合實(shí)例形式分析了net模塊功能及事件監(jiān)聽相關(guān)操作技巧,需要的朋友可以參考下2019-01-01
用nodejs實(shí)現(xiàn)json和jsonp服務(wù)的方法
本篇文章主要介紹了用nodejs實(shí)現(xiàn)json和jsonp服務(wù)的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-08-08
用node和express連接mysql實(shí)現(xiàn)登錄注冊(cè)的實(shí)現(xiàn)代碼
本篇文章主要介紹了用node和express連接mysql實(shí)現(xiàn)登錄注冊(cè)的實(shí)現(xiàn)代碼,具有一定的參考價(jià)值,有興趣的可以了解一下2017-07-07
Node.js API詳解之 vm模塊用法實(shí)例分析
這篇文章主要介紹了Node.js API詳解之 vm模塊用法,結(jié)合實(shí)例形式分析了Node.js API中vm模塊基本功能、函數(shù)、使用方法及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下2020-05-05

