溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Nodejs如何進(jìn)行大文件讀寫(xiě)

發(fā)布時(shí)間:2022-09-29 09:41:44 來(lái)源:億速云 閱讀:140 作者:iii 欄目:web開(kāi)發(fā)

這篇文章主要介紹了Nodejs如何進(jìn)行大文件讀寫(xiě)的相關(guān)知識(shí),內(nèi)容詳細(xì)易懂,操作簡(jiǎn)單快捷,具有一定借鑒價(jià)值,相信大家閱讀完這篇Nodejs如何進(jìn)行大文件讀寫(xiě)文章都會(huì)有所收獲,下面我們一起來(lái)看看吧。

一、node中的文件讀寫(xiě)

1.1 常規(guī)文件讀寫(xiě)

常規(guī)的,如果我們要讀取一個(gè)比較小的文件,可以直接通過(guò):

const fs = require('fs')
let data = fs.readFileSync("./test.png")
console.log(data,123)
//輸出data =

一般而言,同步的方法不是很推薦,因?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 =

這里通過(guò)異步的方法調(diào)用不會(huì)阻塞主線程,多個(gè)文件讀取的IO也可以并行進(jìn)行等。

1.2 Stream文件讀寫(xiě)

常規(guī)的文件讀寫(xiě),我們會(huì)把文件一次性的讀取到內(nèi)存中,這種方法時(shí)間效率和內(nèi)存效率都很低,時(shí)間效率低是指必須要一次性讀取完畢后才能執(zhí)行后續(xù)才做,內(nèi)存效率低是指必須把這個(gè)文件都一次性讀取放入內(nèi)存中,很占用內(nèi)存。因此這種情況下,我們一般使用Stream來(lái)進(jìn)行文件的讀?。?/p>

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 =

通過(guò)Steam來(lái)進(jìn)行文件讀寫(xiě),可以提高內(nèi)存效率和時(shí)間效率。

  • 內(nèi)存效率:在處理數(shù)據(jù)之前,不需要在內(nèi)存中加載大量(或整個(gè))數(shù)據(jù)

  • 時(shí)間效率:一旦有了數(shù)據(jù),就可以開(kāi)始處理,這大大減少開(kāi)始處理數(shù)據(jù)的時(shí)間,而不必等到整個(gè)數(shù)據(jù)加載完畢再進(jìn)行處理。

Stream的文件還支持第二種寫(xiě)法:

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文件讀寫(xiě)RAM和Blob大小的限制

2.1 基礎(chǔ)問(wèn)題

在讀取大文件時(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ì)想到,通過(guò)設(shè)置option,NODE_OPTIONS='--max-old-space-size=5000',此時(shí)5000M>2.5G,但是報(bào)錯(cuò)還是沒(méi)有消失,也就是說(shuō)通過(guò)Options無(wú)法改變node讀取文件的大小限制。

上述是常規(guī)的方式讀取大文件,如果通過(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ì)有異常,不過(guò)要注意的是這邊有一個(gè)報(bào)錯(cuò):

data += chunk;
                ^

RangeError: Invalid string length

此時(shí)是因?yàn)閐ata的長(zhǎng)度超過(guò)了最大限制,比如2048M等。因此在用Steam處理的時(shí)候,在對(duì)讀取結(jié)果的保存時(shí),要注意文件的大小,千萬(wàn)不能超過(guò)默認(rèn)的Buffer的最大值。上述這種情況,我們不用data += chunk將數(shù)據(jù)全部保存在一個(gè)大的data中,我們可以邊讀取邊處理。

2.2 分片讀取

createReadStream在讀取文件的過(guò)程中,其實(shí)也可以分段讀取,這種分段讀取的方法也可以做為大文件讀取的備選項(xiàng)。特別是在并發(fā)讀取的時(shí)候有一定的優(yōu)點(diǎn),可以提升文件讀取和處理的速度。

createReadStream接受第二個(gè)參數(shù){start,end}。我們可以通過(guò)fs.promises.stat來(lái)獲取文件的大小,然后確定分片,最后分片一次讀取,比如:

  1. 獲取文件大小

const info = await fs.promises.stat(filepath)
   const size = info.size

登錄后復(fù)制

  1. 按照指定的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ò)展瀏覽器端的大文件讀寫(xiě)

前面將了大文件在nodejs中的讀取,那么在瀏覽器端會(huì)讀取大文件會(huì)有什么問(wèn)題嗎?

瀏覽器在本地讀取大文件時(shí),之前有類似FileSaver、StreamSaver等方案,不過(guò)在瀏覽器本身添加了File的規(guī)范,使得瀏覽器本身就默認(rèn)和優(yōu)化了Stream的讀取。我們不需要做額外的工作,相關(guān)的工作:github.com/whatwg/fs。不過(guò)不同的版本會(huì)有兼容性的問(wèn)題,我們還是可以通過(guò)FileSaver等進(jìn)行兼容。

3.2 請(qǐng)求靜態(tài)資源大文件

如果是在瀏覽器中獲取靜態(tài)資源大文件,一般情況下只需要通過(guò)range分配請(qǐng)求即可,一般的CDN加速域名,不管是阿里云還是騰訊云,對(duì)于分片請(qǐng)求都支持的很好,我們可以將資源通過(guò)cdn加速,然后在瀏覽器端直接請(qǐng)求cdn加速有的資源。

分片獲取cdn靜態(tài)資源大文件的步驟為,首先通過(guò)head請(qǐng)求獲取文件大?。?/p>

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如何進(jìn)行大文件讀寫(xiě)”這篇文章的內(nèi)容就介紹到這里,感謝各位的閱讀!相信大家對(duì)“Nodejs如何進(jìn)行大文件讀寫(xiě)”知識(shí)都有一定的了解,大家如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI