03、NodeJS-文件操作

一、核心模塊和對(duì)象

  • 核心模塊的意義
  - 如果只是在服務(wù)器運(yùn)行JavaScript代碼,意義并不大,因?yàn)闊o法實(shí)現(xiàn)任何功能(讀寫文件,訪問網(wǎng)絡(luò))。
  - Node 的用處在于它本身還提供的一系列功能模塊,用于與操作系統(tǒng)互動(dòng)。
  - 這些核心的功能模塊在 Node 中內(nèi)置。
  • 常用內(nèi)置模塊
    • path:處理文件路徑
    • fs:操作文件系統(tǒng)
    • child_process:新建子進(jìn)程
    • util:提供一系列實(shí)用小工具
    • http:提供HTTP服務(wù)器功能
    • url:用于解析URL
    • querystring:解析URL中的查詢字符串
    • crypto:提供加密和解密功能
    • 其他

二、文件系統(tǒng)操作

  • 相關(guān)模塊的使用
  - fs: 基礎(chǔ)的文件操作 API
  - path: 提供和路徑相關(guān)的操作 API
  - readline: 用于讀取大文本文件,一行一行讀
  - fs-extra(第三方文件操作模塊): https://www.npmjs.com/package/fs-extra 
  • path模塊的使用
    在文件操作的過程中,都必須使用物理路徑(絕對(duì)路徑),path模塊提供了一系列與路徑相關(guān)的 API;
  // path.basename(path[, ext])
  // 獲取文件名(包含后綴)
  console.log(path.basename(temp));
  // 獲取文件名(不包含后綴) 第二個(gè)參數(shù)即要?jiǎng)h除的后綴
  console.log(path.basename(temp, '.lrc'));
  
  // path.delimiter
  // 獲取不同操作系統(tǒng)中默認(rèn)的路徑分隔符
  // windows是分號(hào),Linux是冒號(hào)
  console.log(path.delimiter);
  // 獲取環(huán)境變量
  console.log(process.env.PATH); 
  // 將對(duì)應(yīng)環(huán)境變量分割
  console.log(process.env.PATH.split(path.delimiter));

  // path.dirname(path)
  // 獲取目錄名稱
  console.log(path.dirname(temp));

  // path.extname(path)
  // 獲取后綴名,包含點(diǎn)(在path.basename中就有ext)
  console.log(path.extname(temp));

  // path.parse(path)
  // 將路徑字符串轉(zhuǎn)為對(duì)象(文件根目錄、文件路徑、文件全名(帶后綴)、后綴名、文件名(不帶后綴))
  console.log(path.parse(temp));

  // path.format(pathObject)
  // 路徑對(duì)象轉(zhuǎn)字符串
  console.log(path.format( path.parse(temp) ));

  // path.isAbsolute(path)
  // 判斷是否為絕對(duì)路徑
  console.log(path.isAbsolute(temp));

  // path.join([...paths])
  // 路徑拼接
  const temp = path.join(__dirname, './lyrics/相依為命.lrc');

  // path.normalize(path)
  // 常規(guī)化一個(gè)路徑(將多余的斜杠處理,)
  var urlStr = 'C:/System32\\a//test///haha.html';
  console.log(path.normalize(urlStr));

  // path.relative(from, to)
  // 獲取to相對(duì)于from的相對(duì)路徑
  console.log(path.relative(__dirname, '/Users/test/Desktop/03-文件系統(tǒng)操作/haha.html'));

  // path.resolve([...paths])
  // 類似于join的字符串拼接
  console.log(path.resolve(__dirname, '../', '../', 'haah.html'));
  // 與join不一樣的(不是簡(jiǎn)單的字符串拼接)
  console.log(path.resolve(__dirname, '/User/test', 'hehe', 'haha.html'));  // 輸出結(jié)果是/User/test/hehe/haha.html
  • 同步或異步調(diào)用
  - fs模塊對(duì)文件的幾乎所有操作都有同步和異步兩種形式
  - 例如:readFile() 和 readFileSync()
  - 同步調(diào)用會(huì)阻塞代碼的執(zhí)行,異步則不會(huì)
  - 異步調(diào)用會(huì)將讀取任務(wù)下達(dá)到任務(wù)隊(duì)列,直到任務(wù)執(zhí)行完成才會(huì)回調(diào)
  - 異常處理方面,同步必須使用 try catch 方式,異步可以通過回調(diào)函數(shù)的第一個(gè)參數(shù)
  • 文件讀取
    • 異步文件讀取
      fs.readFile(file[, options], callback(error, data))
// 讀取時(shí)有設(shè)置編碼utf8
fs.readFile(path.join(__dirname, './test.txt'), 'utf8', (error, data)=>{
      if(error) throw error;
      console.log(`異步: ${data}`);
});
  • 同步文件讀取
    fs.readFileSync(file[, options])
// 同步操作的寫法,錯(cuò)誤是通過拋出異常
try {
      var data = fs.readFileSync(path.join(__dirname, './test.txt'), 'utf8');
      console.log(`同步: ${data}`);
} catch (error) {
      throw  error;
}

node默認(rèn)編碼類型是utf8;
由于Windows平臺(tái)下默認(rèn)文件編碼是GBK,在node中是不支持的gbk類型編碼的!!!!
對(duì)于各種操作系統(tǒng)中編碼問題,可以通過iconv-lite解決

  • 文件流的方式讀取
    fs.createReadStream(path[, options])
const stream = fs.createReadStream('1.txt');
let data = ' '
stream.on('data', (trunk) => {
    data += trunk;
});
stream.on('end', () => {
    console.log(data);
});

流的方式Stream模塊。pipe管道,可以將讀取到流,導(dǎo)入到另外一個(gè)目標(biāo)中,還可以設(shè)置流的類型;
fs.createReadStream(filename)假如該文件流數(shù)據(jù)類型是utf8 通過pipe,將流的類型改變gbk fs.createReadStream(filename).pipe(iconv.decodeStream('gbk'))

  • 模塊逐行讀取文本
    readline
    const readline = require('readline');
    const fs = require('fs');
      
    const rl = readline.createInterface({input: fs.createReadStream('sample.txt') });
    
    rl.on('line', (line) => {
        console.log('Line from file:', line);
  });

正則表達(dá)式: objReg.exec(string) 該函數(shù)通過對(duì)指定你的字符串進(jìn)行一次匹配檢測(cè),獲取字符串中的第一個(gè)與正則表達(dá)式的內(nèi)容,并且將匹配的內(nèi)容和子匹配的結(jié)果存放在返回?cái)?shù)組中
var rel = /^\[\d{2}\:\d{2}\.\d{2,4}\].+$/;
var rel = /^\[(\d{2})\:(\d{2})\.(\d{2,4})\](.+)$/; // 有括號(hào)的即為子分組
rel.exec(line)

> 案例: 歌詞滾動(dòng)

案例: 根據(jù)歌曲時(shí)間顯示對(duì)應(yīng)歌詞

  • 文件寫入
    • 異步文件寫入
      fs.writeFile(file,data[,option],callback(err))
  fs.writeFile(path.join(__dirname, './temp.txt'), JSON.stringify({id: 10}), (err) => {
    if(err){
        // 一般寫文件,如果出現(xiàn)錯(cuò)誤,都是因?yàn)闄?quán)限問題(權(quán)限不夠不能創(chuàng)建文件)
        // 文件夾如果不存在,不會(huì)創(chuàng)建文件夾,也會(huì)出錯(cuò)
        console.log('err:' + err);
    } else {
        console.log('文件寫入成功');
    }
});
  • 同步文件寫入
    fs.writeFileSync(file,data,[,option])
  fs.writeFileSync(path.join(__dirname, 'temp.txt'), '你好?');
  • 流式文件寫
    fs.createWriteStream(path[,option])
fs.createWriteStream();
var streamWriter = fs.createWriteStream(path.join(__dirname, 'temp.txt'));
  // 是屬于非阻塞的streamWriter.write,返回true/false
 console.log( streamWriter.write('哈哈', ()=>{
      console.log('+1');
}) );

默認(rèn)寫入操作是覆蓋源文件

  • pipe管道方式(文件讀寫操作)
var readstream = fs.createReadStream('01-path.js');
var writestream = fs.createWriteStream('test4.txt');
// 直接將讀文件流 導(dǎo)入到 寫文件流中
readstream.pipe(writestream);
  • 異步追加
    fs.appendFile(file,data[,options],callback(err))
fs.appendFile(path.join(__dirname, 'temp.txt'), ' 測(cè)試', (err)=>{
    if(err){
        console.log('error: ' + err);
    } else {
        console.log('追加成功');
    }
});
  • 同步追加
    fs.appendFileSync(file,data[,options])
fs.appendFileSync(path.join(__dirname, 'temp.txt'), ' 追加成功了嗎?');
  • 文件其他操作
    • 獲取文件信息
      fs.stat(path,callback(err,stats))
      fs.statSync(path) // => 返回一個(gè)fs.Stats實(shí)例
fs.stat('temp.txt', (err, stats)=>{
    if(err){
        console.log('err:' + err);
    } else {
        console.log(stats.isFile());
    }
});

讀取到文件信息后,可以判斷文件的類型
stats.isFile()
stats.isDirectory()

stats.isBlockDevice()
stats.isCharacterDevice()
stats.isSymbolicLink()
stats.isSocket()

  • 移動(dòng)文件、重命名文件或目錄
    fs.rename(oldPath,newPath,callback)
    fs.renameSync(oldPath,newPath)
  fs.rename('temp.txt', 'test.txt', (err)=>{
    if(err){
        console.log('err:' + err);
    } else {
        console.log('修改文件名成功');
    }
});
  • 刪除文件
    fs.unlink(path,callback(err))
    fs.unlinkSync(path)
fs.unlink('test.txt', (err)=>{
    if(err){
        console.log('err:' + err);
    } else {
        console.log('刪除文件名成功');
    }
});
  • 目錄操作
    • 創(chuàng)建一個(gè)目錄
      fs.mkdir(path[,model],callback)
      fs.mkdirSync(path[,model])
fs.mkdir(path.join(__dirname, 'test.txt'), (err)=>{
    if(err){
        console.log(err);
    } else {
        console.log('創(chuàng)建文件成功');
    }
});
  • 刪除一個(gè)空目錄
    fs.rmdir(path,callback)
    fs.rmdirSync(path)
fs.rmdir(path.join(__dirname, 'test.txt'), (err)=>{
         if(err){
            console.log('err:' + err);
         }else{
             console.log('刪除成功');
         }
 });
  • 讀取一個(gè)目錄
    fs.readdir(path,callback(err,files))
    fs.readdirSync(path) // => 返回files
fs.readdir(__dirname, (err, files)=>{
           if(err){
            console.log('err:' + err);
           }else{
             console.log(files);
           }
});

三、緩沖區(qū)

  • 什么是緩沖區(qū)
  - 緩沖區(qū)就是內(nèi)存中操作數(shù)據(jù)的容器  
  - 只是數(shù)據(jù)容器而已
  - 通過緩沖區(qū)可以很方便的操作二進(jìn)制數(shù)據(jù)
  - 而且在大文件操作時(shí)必須有緩沖區(qū)
  • 為什么要有緩沖區(qū)
    JavaScript是比較擅長處理字符串,但是早期的應(yīng)用場(chǎng)景主要用于處理HTML文檔,不會(huì)有太大篇幅的數(shù)據(jù)處理,也不會(huì)接觸到二進(jìn)制的數(shù)據(jù)。而在Node中操作數(shù)據(jù)、網(wǎng)絡(luò)通信是沒辦法完全以字符串的方式操作的,所以在Node中引入了一個(gè)二進(jìn)制的緩沖區(qū)的概念:Buffer。

  • 緩沖區(qū)簡(jiǎn)單操作

  // 創(chuàng)建長度為4個(gè)字節(jié)的緩沖區(qū)
  var buffer = new Buffer(4);

  // 通過指定編碼的方式創(chuàng)建
  var buffer = new Buffer('hello', 'utf8');

  // 往緩沖區(qū)中寫數(shù)據(jù)(假如緩沖區(qū)只有4個(gè)字節(jié)大小)
  buffer.write('123456');
  console.log(buffer.toString('utf8'));  // 輸出是1234

node中默認(rèn)的編碼類型都是utf-8;

  • 讀圖片操作
  var fs = require('fs');
  var path = require('path');

  fs.readFile(path.join(__dirname, './test.png'), (error, data)=>{
      if(error) console.error(error);
      // 將buffer內(nèi)容讀取出來(圖片是base64)
      console.log(data.toString('base64'));
  });

還原生成Base64編碼為圖片: http://www.atool.org/img2base64.php;
注意: 要在前面添加上data:image/png;base64,表示圖片類型;

  • node支持的編碼
    Buffers 和 JavaScript 字符串對(duì)象之間轉(zhuǎn)換時(shí)需要一個(gè)明確的編碼方法。
  - 'ascii' - 7位的 ASCII 數(shù)據(jù)。這種編碼方式非常快,它會(huì)移除最高位內(nèi)容
  - 'utf8' - 多字節(jié)編碼 Unicode 字符。大部分網(wǎng)頁和文檔使用這類編碼方式
  - 'utf16le' - 2個(gè)或4個(gè)字節(jié), Little Endian (LE) 編碼 Unicode 字符。編碼范圍 (U+10000 到 U+10FFFF) 
  - 'ucs2' - 'utf16le'的子集
  - 'base64' - Base64 字符編碼
  - 'binary' - 僅使用每個(gè)字符的頭8位將原始的二進(jìn)制信息進(jìn)行編碼
(在需使用 Buffer 的情況下,應(yīng)該盡量避免使用這個(gè)已經(jīng)過時(shí)的編碼方式,這個(gè)編碼方式將會(huì)在未來某個(gè)版本中棄用)。
  - 'hex' - 每個(gè)字節(jié)都采用 2 進(jìn)制編碼。

在node中是不支持的gbk類型編碼的!!!!
對(duì)于各種操作系統(tǒng)中編碼問題,可以使用第三方模塊iconv-lite

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • //公共引用 varfs =require('fs'), path =require('path'); 1、讀取文...
    才気莮孒閱讀 840評(píng)論 0 1
  • 個(gè)人入門學(xué)習(xí)用筆記、不過多作為參考依據(jù)。如有錯(cuò)誤歡迎斧正 目錄 簡(jiǎn)書好像不支持錨點(diǎn)、復(fù)制搜索(反正也是寫給我自己看...
    kirito_song閱讀 2,500評(píng)論 1 37
  • 文件系統(tǒng)模塊是一個(gè)封裝了標(biāo)準(zhǔn)的 POSIX 文件 I/O 操作的集合。通過require('fs')使用這個(gè)模塊。...
    保川閱讀 802評(píng)論 0 0
  • Buffer (緩沖) path (路徑) file (文件是否)
    悟空你又瘦了閱讀 292評(píng)論 0 0
  • 文|東東 1. 少青爺老了(與世長辭)。 得知這一消息時(shí),我正在準(zhǔn)備研究生畢業(yè)答辯,父親就沒讓我回去。 我記事起,...
    郝東東閱讀 328評(píng)論 0 0