站長資訊網
        最全最豐富的資訊網站

        淺析Nodejs怎么進行大文件讀寫

        淺析Nodejs怎么進行大文件讀寫

        node.js極速入門課程:進入學習

        筆者最近在做一些node端的文件讀寫和分片上傳工作,在這個過程中,發現node讀取的文件如果超過2G,超過了讀取Blob最大值,會出現讀取異常,此外在node中讀寫文件也受服務器RAM的限制等,需要分片讀取,本人記錄一下遇到的問題以及解決問題的經過。【相關教程推薦:nodejs視頻教程】

        • node中的文件讀寫
        • node文件讀寫RAM和Blob大小的限制
        • 其他

        一、node中的文件讀寫

        1.1 常規文件讀寫

        常規的,如果我們要讀取一個比較小的文件,可以直接通過:

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

        一般而言,同步的方法不是很推薦,因為js/nodejs是單線程的,同步的方法會阻塞主線程。最新版的node直接提供了fs.promise,可以結合async/await直接使用:

        const fs = require('fs') const readFileSync = async () => {     let data = await fs.promises.readFile("./test.png")     console.log(data,123) } readFileSync() //輸出data = 
        登錄后復制

        這里通過異步的方法調用不會阻塞主線程,多個文件讀取的IO也可以并行進行等。

        1.2 Stream文件讀寫

        常規的文件讀寫,我們會把文件一次性的讀取到內存中,這種方法時間效率和內存效率都很低,時間效率低是指必須要一次性讀取完畢后才能執行后續才做,內存效率低是指必須把這個文件都一次性讀取放入內存中,很占用內存。因此這種情況下,我們一般使用Stream來進行文件的讀取:

        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 = 
        登錄后復制

        通過Steam來進行文件讀寫,可以提高內存效率和時間效率。

        • 內存效率:在處理數據之前,不需要在內存中加載大量(或整個)數據
        • 時間效率:一旦有了數據,就可以開始處理,這大大減少開始處理數據的時間,而不必等到整個數據加載完畢再進行處理。

        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 基礎問題

        在讀取大文件時,會有讀取文件大小的限制,比如我們現在在讀取一個2.5G的視頻文件:

        const fs = require('fs') const readFileTest = async () => {     let data = await fs.promises.readFile("./video.mp4")     console.log(data) } readFileTest()
        登錄后復制

        執行上述的代碼會報錯:

        RangeError [ERR_FS_FILE_TOO_LARGE]: File size (2246121911) is greater than 2 GB

        我們可能會想到,通過設置option,NODE_OPTIONS='–max-old-space-size=5000',此時5000M>2.5G,但是報錯還是沒有消失,也就是說通過Options無法改變node讀取文件的大小限制。

        上述是常規的方式讀取大文件,如果通過Steam的方式讀取還會有文件大小的限制嘛? 比如:

        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()
        登錄后復制

        如上方式讀取一個2.5G的文件不會有異常,不過要注意的是這邊有一個報錯:

        data += chunk;                 ^  RangeError: Invalid string length
        登錄后復制

        此時是因為data的長度超過了最大限制,比如2048M等。因此在用Steam處理的時候,在對讀取結果的保存時,要注意文件的大小,千萬不能超過默認的Buffer的最大值。上述這種情況,我們不用data += chunk將數據全部保存在一個大的data中,我們可以邊讀取邊處理。

        2.2 分片讀取

        createReadStream在讀取文件的過程中,其實也可以分段讀取,這種分段讀取的方法也可以做為大文件讀取的備選項。特別是在并發讀取的時候有一定的優點,可以提升文件讀取和處理的速度。

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

        1. 獲取文件大小

        const info = await fs.promises.stat(filepath)    const size = info.size
        登錄后復制

        1. 按照指定的SIZE分片(比如128M一個分片)

          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.實現讀取函數

        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個bit。

        三、其他

        3.1 擴展瀏覽器端的大文件讀寫

        前面將了大文件在nodejs中的讀取,那么在瀏覽器端會讀取大文件會有什么問題嗎?

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

        3.2 請求靜態資源大文件

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

        分片獲取cdn靜態資源大文件的步驟為,首先通過head請求獲取文件大小:

        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屬性中,獲取文件的大小。然后進行分片和分段,最后發起range請求:

        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},就可以發起分片請求去獲取分段資源,這里的start和end也是前閉后閉的。

        贊(0)
        分享到: 更多 (0)
        網站地圖   滬ICP備18035694號-2    滬公網安備31011702889846號
        主站蜘蛛池模板: 亚洲精品综合久久| 亚洲国产精品久久久久网站| 精品欧美一区二区在线观看| 亚洲精品夜夜夜妓女网| 久久国产精品久久精品国产| 中文字幕av日韩精品一区二区| 国产精品黄页免费高清在线观看| 996久久国产精品线观看| 亚洲乱码精品久久久久..| 精品国产三级a乌鸦在线观看| 精品国产免费一区二区三区香蕉| 奇米精品视频一区二区三区| 欧美XXXX黑人又粗又长精品| 国产精品电影在线| 久久精品国产精品亚洲精品| 国产精品99精品久久免费| 亚洲AV无码久久精品狠狠爱浪潮| 久久久久亚洲精品中文字幕| 国产欧美在线观看精品一区二区| 久久成人影院精品777| 2021国产精品成人免费视频| 免费精品无码AV片在线观看| 亚洲一区二区三区国产精品| 香港三级精品三级在线专区| 精品无码人妻一区二区三区不卡| 国产成人精品曰本亚洲79ren| 日本人精品video黑人| 精品国产欧美一区二区| 成人午夜精品视频在线观看| 国产精品亚洲成在人线| 久久亚洲私人国产精品vA| 无码欧精品亚洲日韩一区| 亚洲精品无码久久久久sm| 中文字幕久精品免费视频| 亚洲精品高清在线| 亚洲日韩精品一区二区三区 | 国产精品高清在线| AAA级久久久精品无码区| 国产成人99久久亚洲综合精品| 麻豆精品不卡国产免费看| 欧美精品一本久久男人的天堂 |