Nodejs API 學(xué)習(xí)系列(一)

本文的主要內(nèi)容是對(duì)nodejs提供的一些重要模塊蜕琴,結(jié)合官方API進(jìn)行介紹莱预,遇到精彩的文章抛寝,我會(huì)附在文中并標(biāo)明了出處氧枣。主要包括如下7個(gè)模塊

  • path 模塊
  • http 模塊
  • fs 模塊
  • url 模塊
  • query string 模塊
  • os 模塊
  • stream 模塊

轉(zhuǎn)載請(qǐng)注明出處沐兵,多謝支持~

path 路徑相關(guān)模塊

模塊簡(jiǎn)介

nodejs path 模塊提供了一些用于處理文件路徑的工具函數(shù),我們可以通過(guò)如下方式引用它

 var path = require("path")

path.normalize(p)

標(biāo)準(zhǔn)化路徑字符串便监,處理冗余的“..”扎谎、“.”、“/”字符:

原則:

  • 對(duì)window系統(tǒng)烧董,目錄分隔為'',對(duì)于UNIX系統(tǒng)毁靶,分隔符為'/',針對(duì)'..'返回上一級(jí)逊移;/與\都被統(tǒng)一轉(zhuǎn)換path.normalize(p);

  • 如果路徑為空预吆,返回.,相當(dāng)于當(dāng)前的工作路徑胳泉。

  • 將對(duì)路徑中重復(fù)的路徑分隔符(比如linux下的/)合并為一個(gè)拐叉。

  • 對(duì)路徑中的.、..進(jìn)行處理扇商。(類似于shell里的cd ..)

  • 如果路徑最后有/凤瘦,那么保留該/。

    var url1 = path.normalize('a/b/c/../user/vajoy/bin');
    var url2 = path.normalize('a/b/c///../user/vajoy/bin/');
    var url3 = path.normalize('a/b/c/../../user/vajoy/bin');
    var url4 = path.normalize('a/b/c/.././///../user/vajoy/bin/..');
    var url5 = path.normalize('a/b/c/../../user/vajoy/bin/../../');
    var url6 = path.normalize('a/../../user/vajoy/bin/../../');
    var url7 = path.normalize('a/../../user/vajoy/bin/../../../../');
    var url8 = path.normalize('./a/.././user/vajoy/bin/./');

    console.log('url1:',url1); // a\b\user\vajoy\bin
    console.log('url2:',url2); // a\b\user\vajoy\bin
    console.log('url3:',url3); // a\user\vajoy\bin
    console.log('url4:',url4); // a\user\vajoy
    console.log('url5:',url5); // a\user
    console.log('url6:',url6); // ..\user
    console.log('url7:',url7); // ....
    console.log('url8:',url8); // user\vajoy\bin\

path.join([path1], [path2], [...])

將多個(gè)路徑結(jié)合在一起钳吟,并轉(zhuǎn)換為標(biāo)準(zhǔn)化的路徑

var url1 = path.join('////./a', 'b////c', 'user/', 'vajoy', '..');
var url2 = path.join('a', '../../', 'user/', 'vajoy', '..');
var url3 = path.join('a', '../../', {}, 'vajoy', '..');
var url4 = path.join('path1', 'path2//pp\\', ../path3');

console.log('url1:',url1);  // \a\b\c\user
console.log('url2:',url2);  // ..\user
console.log('url3:',url3);  // 存在非路徑字符串廷粒,故拋出異常
console.log('url4:',url4);  // path1\path2\path3

path.resolve([from ...], to)

從源地址 from 到目的地址 to 的絕對(duì)路徑

原則
以應(yīng)用程序根目錄為起點(diǎn),根據(jù)參數(shù)字符串解析出一個(gè)絕對(duì)路徑
要注意的是,如果某個(gè) from 或 to 參數(shù)是絕對(duì)路徑(比如 'E:/abc'坝茎,或是以“/”開頭的路徑)涤姊,則將忽略之前的 from 參數(shù)。

// 下文中app的根目錄為D:\temp\test
var url1 = path.resolve('.', 'testFiles/..', 'trdLayer');
var url2 = path.resolve('..', 'testFiles', 'a.txt');
var url3 = path.resolve('D:/vajoy', 'abc', 'D:/a');
var url4 = path.resolve('abc', 'vajoy', 'ok.gif');
var url5 = path.resolve('abc', '/vajoy', '..', 'a/../subfile'); //'abc'參數(shù)將被忽略嗤放,源路徑改從'D:/vajoy'開始

console.log('url1:',url1);  //D:\temp\test\trdLayer
console.log('url2:',url2);  //D:\temp\testFiles\a.txt
console.log('url3:',url3);  //D:\a
console.log('url4:',url4);  //D:\temp\test\abc\vajoy\ok.gif
console.log('url5:',url5);  //D:\subfile

path.relative(from, to)

獲取從 from 到 to 的相對(duì)路徑(即思喊,基于from路徑的兩路徑間的相互關(guān)系),可以看作 path.resolve 的相反實(shí)現(xiàn)

var url1 = path.relative('C:\\vajoy\\test\\aaa', 'C:\\vajoy\\impl\\bbb');
var url2 = path.relative('C:/vajoy/test/aaa', 'C:/vajoy/bbb');
var url3 = path.relative('C:/vajoy/test/aaa', 'D:/vajoy/bbb');

console.log('url1:',url1);  //..\..\impl\bbb
console.log('url2:',url2);  //..\..\bbb
console.log('url3:',url3);  //D:\vajoy\bbb
  • 如果from次酌、to指向同個(gè)路徑恨课,那么,返回空字符串岳服。
  • 如果from剂公、to中任一者為空,那么吊宋,返回當(dāng)前工作路徑纲辽。

path.isAbsolute(path)

判斷 path 是否絕對(duì)路徑。這塊可以理解為璃搜,path 是否真的是一個(gè)絕對(duì)路徑(比如 'E:/abc')拖吼,或者是以“/”開頭的路徑,二者都會(huì)返回true

var url1 = path.isAbsolute('../testFiles/secLayer');
var url2 = path.isAbsolute('./join.js');
var url3 = path.isAbsolute('temp');
var url4 = path.isAbsolute('/temp/../..');
var url5 = path.isAbsolute('E:/github/nodeAPI/abc/efg');
var url6 = path.isAbsolute('///temp123');

console.log('url1:',url1);  // false
console.log('url2:',url2);  // false
console.log('url3:',url3);  // false
console.log('url4:',url4);  // true
console.log('url5:',url5);  // true
console.log('url6:',url6);  // true

path.dirname(p)

返回路徑中文件夾的路徑

var url1 = path.dirname('/foo/bar/baz/asdf/a.txt');
var url2 = path.dirname('/foo/bar/baz/asdf/');
var url3 = path.dirname('C:/vajoy/test/aaa');
var url4 = path.dirname(__dirname + '/docs/a.txt')

console.log('url1:',url1);  // /foo/bar/baz/asdf
console.log('url2:',url2);  // /foo/bar/baz
console.log('url3:',url3);  // C:/vajoy/test
console.log(url4);// D:\mobileWeb\temp\test/docs

path.basename(p, [ext])

返回路徑中的最后一部分(通常為文件名)这吻,類似于Unix 的 basename 命令吊档。 ext 為需要截掉的尾綴內(nèi)容

var url1 = path.basename('/foo/bar/baz/asdf/a.txt');
var url2 = path.basename('/foo/bar/baz/asdf/a.txt','.txt');
var url3 = path.basename('/foo/bar/baz/asdf/');
var url4 = path.basename('C:/vajoy/test/aaa');

console.log('url1:',url1);  // a.txt
console.log('url2:',url2);  // a
console.log('url3:',url3);  // asdf
console.log('url4:',url4);  // aaa

path.extname(p)

返回路徑文件中的擴(kuò)展名(若存在)

var url1 = path.extname('/foo/bar/baz/asdf/a.txt');
var url2 = path.extname('/foo/bar/baz/asdf/a.txt.html');
var url3 = path.extname('/foo/bar/baz/asdf/a.');
var url4 = path.extname('C:/vajoy/test/.');
var url5 = path.extname('C:/vajoy/test/a');

console.log('url1:',url1);  // .txt
console.log('url2:',url2);  // .html
console.log('url3:',url3);  // .
console.log('url4:',url4);  //
console.log('url5:',url5);  //

path.parse(pathString)

返回路徑字符串的對(duì)象

var url1 = path.parse('/foo/bar/baz/asdf/a.txt');
url1: { 
  root: '/',//根目錄  
  dir: '/foo/bar/baz/asdf',//文件所在目錄 
  base: 'a.txt',//文件名,輸出文件名稱以base為準(zhǔn),base為空唾糯,則不輸出文件名 
  ext: '.txt',//文件擴(kuò)展名
  name: 'a',//文件名稱, 不含擴(kuò)展名(name返回的是文件名或最后文件夾名)
}

var url2=path.parse('C:\\path\\dir\\');  
{ root: 'C:\\',  
  dir: 'C:\\path',  
  base: 'dir',  
  ext: '',  
  name: 'dir'   
} 

var url3=path.format({  
    root:'f:',  
    dir:'f:\\dir1\\dir2',  
    name:'file', 
    base:'file.nanme', 
    ext:'.txt'  
}); 
//f:\dir1\dir2\file.nanme

var url3=path.format({  
    root:'f:',  
    dir:'f:\\dir1\\dir2',  
    name:'file', 
    ext:'.txt'  
}); 
//f:\dir1\dir2\

path.format(pathObject)

從對(duì)象中返回路徑字符串怠硼,和 path.parse 相反

var pathObj =  { 
      root: '/',
      dir: '/foo/bar/baz/asdf',
      base: 'a.txt',
      ext: '.txt',
      name: 'a' 
}

var url1 = path.format(pathObj);
console.log('url1:',url1);//url1: /foo/bar/baz/asdf\a.txt

path.sep

返回對(duì)應(yīng)平臺(tái)下的文件夾分隔符,win下為''趾断,nix下為'/'*

var url1 = path.sep;
var url2 = 'foo\\bar\\baz'.split(path.sep);
var url3 = 'foo/bar/baz'.split(path.sep);

console.log('url1:',url1);  // win下為\拒名,*nix下為/
console.log('url2:',url2);  // [ 'foo', 'bar', 'baz' ]?
console.log('url3:',url3);  // win下返回[ 'foo/bar/baz' ],但在*nix系統(tǒng)下會(huì)返回[ 'foo', 'bar', 'baz' ]

path.delimiter

返回對(duì)應(yīng)平臺(tái)下的路徑分隔符芋酌,win下為';'增显,nix下為':'*

var env = process.env.PATH; //當(dāng)前系統(tǒng)的環(huán)境變量PATH

var url1 = env.split(path.delimiter);

console.log(path.delimiter); 
//win下為“;”,*nix下為“:”
console.log('env:',env); 
// C:\ProgramData\Oracle\Java\javapath;C:\Program Files (x86)\Intel\iCLS Client\;
console.log('url1:',url1);  
// ['C:\ProgramData\Oracle\Java\javapath','C:\Program Files (x86)\Intel\iCLS Client\']

http 網(wǎng)絡(luò)請(qǐng)求模塊

推薦文章

fs 文件系統(tǒng)操作模塊

模塊簡(jiǎn)介

nodejs path 模塊提供了一些用于處理文件系統(tǒng)的小工具脐帝,我們可以通過(guò)如下方式引用它

 var path = require("fs")

同步&&異步API

使用require('fs')載入fs模塊同云,模塊中所有方法都有同步和異步兩種形式。

異步方法中回調(diào)函數(shù)的第一個(gè)參數(shù)總是留給異常參數(shù)(exception)堵腹,如果方法成功完成炸站,該參數(shù)為null或undefined。

fs.readFile('./test.txt', function(err, data) {
    if (err) throw err;
    console.log('文件內(nèi)容:'+ data);
});

同步寫法疚顷,一般都是在異步方法名后拼接Sycn字符串旱易,表示是同步方法

var data = fs.readFileSync('./test.txt');
console.log('文件內(nèi)容:'+ data);

同步方法執(zhí)行完并返回結(jié)果后禁偎,才能執(zhí)行后續(xù)的代碼。而異步方法采用回調(diào)函數(shù)接收返回結(jié)果阀坏,可以立即執(zhí)行后續(xù)代碼如暖。下面的代碼演示,都已異步邏輯為主忌堂。

fs.readFile

/**
 * filename, 必選參數(shù)盒至,文件名
 * [options],可選參數(shù),可指定flag(文件操作選項(xiàng)士修,如r+ 讀寫枷遂;w+ 讀寫,文件不存在則創(chuàng)建)及encoding屬性
 * callback 讀取文件后的回調(diào)函數(shù)棋嘲,參數(shù)默認(rèn)第一個(gè)err,第二個(gè)data 數(shù)據(jù)
 */

fs.readFile(__dirname + '/test.txt', {flag: 'r+', encoding: 'utf8'}, function (err, data) {
    if(err) throw err;
    console.log(data);
});

fs.writeFile

var w_data = '這是一段通過(guò)fs.writeFile函數(shù)寫入的內(nèi)容酒唉;\r\n';
//w_data = new Buffer(w_data);//可以將字符串轉(zhuǎn)換成Buffer類型

/**
 * filename, 必選參數(shù),文件名
 * data, 寫入的數(shù)據(jù)沸移,可以字符或一個(gè)Buffer對(duì)象
 * [options],flag,mode(權(quán)限),encoding
 * callback 讀取文件后的回調(diào)函數(shù)黔州,參數(shù)默認(rèn)第一個(gè)err,第二個(gè)data 數(shù)據(jù)
 */

fs.writeFile(__dirname + '/test.txt', w_data, {flag: 'a'}, function (err) {

    if(err) throw err;
    console.log('寫入成功');

});

{flag: 'a'} 加上這個(gè)參數(shù),內(nèi)容將會(huì)被以追加方式寫入文件阔籽,不加上這個(gè)參數(shù)則會(huì)先清空內(nèi)容,再寫入數(shù)據(jù)

fs.open(filename, flags, [mode], callback);

/**
 * filename, 必選參數(shù)牲蜀,文件名
 * flags, 操作標(biāo)識(shí)笆制,如"r",讀方式打開
 * [mode],權(quán)限,如777涣达,表示任何用戶讀寫可執(zhí)行
 * callback 打開文件后回調(diào)函數(shù)在辆,參數(shù)默認(rèn)第一個(gè)err,第二個(gè)fd為一個(gè)整數(shù),表示打開文件返回的文件描述符度苔,window中又稱文件句柄
 */

fs.open(__dirname + '/test.txt', 'r', '0666', function (err, fd) {
  console.log(fd);
});

fs.read(fd, buffer, offset, length, position, callback);

講文件內(nèi)容讀入緩存區(qū)
/**
* fd, 使用fs.open打開成功后返回的文件描述符
* buffer, 一個(gè)Buffer對(duì)象匆篓,v8引擎分配的一段內(nèi)存
* offset, 整數(shù),向緩存區(qū)中寫入時(shí)的初始位置寇窑,以字節(jié)為單位
* length, 整數(shù)鸦概,讀取文件的長(zhǎng)度
* position, 整數(shù),讀取文件初始位置甩骏;文件大小以字節(jié)為單位
* callback(err, bytesRead, buffer), 讀取執(zhí)行完成后回調(diào)函數(shù)窗市,bytesRead實(shí)際讀取字節(jié)數(shù),被讀取的緩存區(qū)對(duì)象
*/

fs.open(__dirname + '/test.txt', 'r', function (err, fd) {
  if(err) {
    console.error(err);
    return;
  } else {
    var buffer = new Buffer(255);
    console.log(buffer.length);
    //每一個(gè)漢字utf8編碼是3個(gè)字節(jié)饮笛,英文是1個(gè)字節(jié)
    fs.read(fd, buffer, 0, 9, 3, function (err, bytesRead, buffer) {
      if(err) {
        throw err;
      } else {
        console.log(bytesRead);
        console.log(buffer.slice(0, bytesRead).toString());
        //讀取完后咨察,再使用fd讀取時(shí),基點(diǎn)是基于上次讀取位置計(jì)算福青;
        fs.read(fd, buffer, 0, 9, null, function (err, bytesRead, buffer) {
          console.log(bytesRead);
          console.log(buffer.slice(0, bytesRead).toString());
        });
      }
    });
  }
});

fs.write(fd, buffer, offset, length, position, callback);

寫文件摄狱,將緩沖區(qū)內(nèi)數(shù)據(jù)寫入使用fs.open打開的文件

/**
 * fd, 使用fs.open打開成功后返回的文件描述符
 * buffer, 一個(gè)Buffer對(duì)象脓诡,v8引擎分配的一段內(nèi)存
 * offset, 整數(shù),從緩存區(qū)中讀取時(shí)的初始位置媒役,以字節(jié)為單位
 * length, 整數(shù)祝谚,從緩存區(qū)中讀取數(shù)據(jù)的字節(jié)數(shù)
 * position, 整數(shù),寫入文件初始位置刊愚;
 * callback(err, written, buffer), 寫入操作執(zhí)行完成后回調(diào)函數(shù)踊跟,written實(shí)際寫入字節(jié)數(shù),buffer被讀取的緩存區(qū)對(duì)象
 */

fs.open(__dirname + '/test.txt', 'a', function (err, fd) {
  if(err) {
    console.error(err);
    return;
  } else {
    var buffer = new Buffer('寫入文件數(shù)據(jù)內(nèi)容');
    //寫入'入文件'三個(gè)字
    fs.write(fd, buffer, 3, 9, 12, function (err, written, buffer) {
      if(err) {
        console.log('寫入文件失敗');
        console.error(err);
        return;
      } else {
        console.log(buffer.toString());
        //寫入'數(shù)據(jù)內(nèi)'三個(gè)字
        fs.write(fd, buffer, 12, 9, null, function (err, written, buffer) {
          console.log(buffer.toString());
          // 使用fs.write寫入文件時(shí)鸥诽,操作系統(tǒng)是將數(shù)據(jù)讀到內(nèi)存商玫,再把數(shù)據(jù)寫入到文件中,所以當(dāng)數(shù)據(jù)讀完時(shí)并不代表數(shù)據(jù)已經(jīng)寫完牡借,因?yàn)橛幸徊糠诌€可能在內(nèi)在緩沖區(qū)內(nèi)拳昌。
          // 因此可以使用fs.fsync方法將內(nèi)存中數(shù)據(jù)寫入文件,刷新內(nèi)存緩沖區(qū)钠龙;
          fs.fsync(fd);
          fs.close(fd);
        })
      }
    });
  }
});

fs.mkdir(path, [mode], callback)

創(chuàng)建目錄

/**
 * path, 被創(chuàng)建目錄的完整路徑及目錄名炬藤;
 * [mode], 目錄權(quán)限,默認(rèn)0777
 * [callback(err)], 創(chuàng)建完目錄回調(diào)函數(shù),err錯(cuò)誤對(duì)象
 */

fs.mkdir(__dirname + '/fsDir', function (err) {
  if(err)
    throw err;
  console.log('創(chuàng)建目錄成功')
});

fs.readdir(path, callback)

讀取目錄

/**
 * path, 要讀取目錄的完整路徑及目錄名碴里;
 * [callback(err, files)], 讀完目錄回調(diào)函數(shù)沈矿;err錯(cuò)誤對(duì)象,files數(shù)組咬腋,存放讀取到的目錄中的所有文件名
 */

fs.readdir(__dirname + '/fsDir/', function (err, files) {
  if(err) {
    console.error(err);
    return;
  } else {
    files.forEach(function (file) {
      var filePath = path.normalize(__dirname + '/fsDir/' + file);
      fs.stat(filePath, function (err, stat) {
        if(stat.isFile()) {
          console.log(filePath + ' is: ' + 'file');
        }
        if(stat.isDirectory()) {
          console.log(filePath + ' is: ' + 'dir');
        }
      });
    });
  }
});

fs.stat(path, callback);

查看文件與目錄信息

fs.stat(__dirname + '/test.txt', function (err, stat) {
    console.log('訪問(wèn)時(shí)間: ' + stat.atime.toString() + '; \n修改時(shí)間:' + stat.mtime);
    console.log(stat.mode);
  })

fs.exists(path, callback);

查看文件與目錄是否存在

/**
 * path, 要查看目錄/文件的完整路徑及名羹膳;
 * [callback(exists)], 操作完成回調(diào)函數(shù);exists true存在根竿,false表示不存在
 */

fs.exists(__dirname + '/test', function (exists) {
  var retTxt = exists ? retTxt = '文件存在' : '文件不存在';
  console.log(retTxt);
});

fs.rename(oldPath, newPath, callback);

移動(dòng)/重命名文件或目錄

/**
 * oldPath, 原目錄/文件的完整路徑及名陵像;
 * newPath, 新目錄/文件的完整路徑及名;如果新路徑與原路徑相同寇壳,而只文件名不同醒颖,則是重命名
 * [callback(err)], 操作完成回調(diào)函數(shù);err操作失敗對(duì)象
 */
fs.rename(__dirname + '/test', __dirname + '/fsDir', function (err) {
  if(err) {
    console.error(err);
    return;
  }
  console.log('重命名成功')
});

fs.rmdir(path, callback);

刪除空目錄

/**
 * path, 目錄的完整路徑及目錄名壳炎;
 * [callback(err)], 操作完成回調(diào)函數(shù)泞歉;err操作失敗對(duì)象
 */
fs.mkdir(__dirname + '/test', function(err){
    fs.rmdir(__dirname + '/test', function (err) {
      if(err) {
        console.log('刪除空目錄失敗,可能原因:1匿辩、目錄不存在疏日,2、目錄不為空')
        console.error(err);
        return;
      }
      console.log('刪除空目錄成功!');
    });
}) 

fs.watchFile(filename, [options], listener);

對(duì)文件進(jìn)行監(jiān)視撒汉,并且在監(jiān)視到文件被修改時(shí)執(zhí)行處理

/**
 * filename, 完整路徑及文件名沟优;
 * [options], persistent true表示持續(xù)監(jiān)視,不退出程序睬辐;interval 單位毫秒挠阁,表示每隔多少毫秒監(jiān)視一次文件
 * listener, 文件發(fā)生變化時(shí)回調(diào)宾肺,有兩個(gè)參數(shù):curr為一個(gè)fs.Stat對(duì)象,被修改后文件侵俗,prev,一個(gè)fs.Stat對(duì)象锨用,表示修改前對(duì)象
 */
fs.watchFile(__dirname + '/test.txt', {interval: 20}, function (curr, prev) {
  if(Date.parse(prev.ctime) == 0) {
    console.log('文件被創(chuàng)建!');//?
  } else if(Date.parse(curr.ctime) == 0) {
    console.log('文件被刪除!')
  } else if(Date.parse(curr.mtime) != Date.parse(prev.mtime)) {
    console.log('文件有修改');
  }
});
fs.watchFile(__dirname + '/test.txt', function (curr, prev) {
  console.log('這是第二個(gè)watch,監(jiān)視到文件有修改');
});

fs.watch(filename, [options], [listener]);

對(duì)文件或目錄進(jìn)行監(jiān)視,并且在監(jiān)視到修改時(shí)執(zhí)行處理隘谣;
fs.watch返回一個(gè)fs.FSWatcher對(duì)象增拥,擁有一個(gè)close方法,用于停止watch操作寻歧;
當(dāng)fs.watch有文件變化時(shí)掌栅,會(huì)觸發(fā)fs.FSWatcher對(duì)象的change(err, filename)事件,err錯(cuò)誤對(duì)象码泛,filename發(fā)生變化的文件名

/**
 * filename, 完整路徑及文件名或目錄名猾封;
 * [listener(event, filename], 監(jiān)聽器事件,有兩個(gè)參數(shù):event 為rename表示指定的文件或目錄中有重命名噪珊、刪除或移動(dòng)操作或change表示有修改晌缘,filename表示發(fā)生變化的文件路徑
 */

var fsWatcher = fs.watch(__dirname + '/test', function (event, filename) {
  //console.log(event)
});

//console.log(fsWatcher instanceof FSWatcher);

fsWatcher.on('change', function (event, filename) {
  console.log(filename + ' 發(fā)生變化')
});

//30秒后關(guān)閉監(jiān)視
setTimeout(function () {
  console.log('關(guān)閉')
  fsWatcher.close(function (err) {
    if(err) {
      console.error(err)
    }
    console.log('關(guān)閉watch')
  });
}, 30000);    

文件流

 /*
 * 流,在應(yīng)用程序中表示一組有序的痢站、有起點(diǎn)有終點(diǎn)的字節(jié)數(shù)據(jù)的傳輸手段磷箕;
 * Node.js中實(shí)現(xiàn)了stream.Readable/stream.Writeable接口的對(duì)象進(jìn)行流數(shù)據(jù)讀寫;以上接口都繼承自EventEmitter類阵难,因此在讀/寫流不同狀態(tài)時(shí)搀捷,觸發(fā)不同事件;
 * 關(guān)于流讀榷嗤:Node.js不斷將文件一小塊內(nèi)容讀入緩沖區(qū),再?gòu)木彌_區(qū)中讀取內(nèi)容氢烘;
 * 關(guān)于流寫入:Node.js不斷將流數(shù)據(jù)寫入內(nèi)在緩沖區(qū)怀偷,待緩沖區(qū)滿后再將緩沖區(qū)寫入到文件中;重復(fù)上面操作直到要寫入內(nèi)容寫寫完播玖;
 * readFile椎工、read、writeFile蜀踏、write都是將整個(gè)文件放入內(nèi)存而再操作维蒙,而則是文件一部分?jǐn)?shù)據(jù)一部分?jǐn)?shù)據(jù)操作;
 *
 * -----------------------流讀取-------------------------------------
 * 讀取數(shù)據(jù)對(duì)象:
 * fs.ReadStream 讀取文件
 * http.IncomingMessage 客戶端請(qǐng)求或服務(wù)器端響應(yīng)
 * net.Socket    Socket端口對(duì)象
 * child.stdout  子進(jìn)程標(biāo)準(zhǔn)輸出
 * child.stdin   子進(jìn)程標(biāo)準(zhǔn)入
 * process.stdin 用于創(chuàng)建進(jìn)程標(biāo)準(zhǔn)輸入流
 * Gzip果覆、Deflate颅痊、DeflateRaw   數(shù)據(jù)壓縮
 *
 * 觸發(fā)事件:
 * readable  數(shù)據(jù)可讀時(shí)
 * data      數(shù)據(jù)讀取后
 * end       數(shù)據(jù)讀取完成時(shí)
 * error     數(shù)據(jù)讀取錯(cuò)誤時(shí)
 * close     關(guān)閉流對(duì)象時(shí)
 *
 * 讀取數(shù)據(jù)的對(duì)象操作方法:
 * read      讀取數(shù)據(jù)方法
 * setEncoding   設(shè)置讀取數(shù)據(jù)的編
 * pause     通知對(duì)象眾目停止觸發(fā)data事件
 * resume    通知對(duì)象恢復(fù)觸發(fā)data事件
 * pipe      設(shè)置數(shù)據(jù)通道,將讀入流數(shù)據(jù)接入寫入流局待;
 * unpipe    取消通道
 * unshift   當(dāng)流數(shù)據(jù)綁定一個(gè)解析器時(shí)斑响,此方法取消解析器
 *
 * ------------------------流寫入-------------------------------------
 * 寫數(shù)據(jù)對(duì)象:
 * fs.WriteStream           寫入文件對(duì)象
 * http.clientRequest       寫入HTTP客戶端請(qǐng)求數(shù)據(jù)
 * http.ServerResponse      寫入HTTP服務(wù)器端響應(yīng)數(shù)據(jù)
 * net.Socket               讀寫TCP流或UNIX流菱属,需要connection事件傳遞給用戶
 * child.stdout             子進(jìn)程標(biāo)準(zhǔn)輸出
 * child.stdin              子進(jìn)程標(biāo)準(zhǔn)入
 * Gzip、Deflate舰罚、DeflateRaw  數(shù)據(jù)壓縮
 *
 * 寫入數(shù)據(jù)觸發(fā)事件:
 * drain            當(dāng)write方法返回false時(shí)纽门,表示緩存區(qū)中已經(jīng)輸出到目標(biāo)對(duì)象中,可以繼續(xù)寫入數(shù)據(jù)到緩存區(qū)
 * finish           當(dāng)end方法調(diào)用营罢,全部數(shù)據(jù)寫入完成
 * pipe             當(dāng)用于讀取數(shù)據(jù)的對(duì)象的pipe方法被調(diào)用時(shí)
 * unpipe           當(dāng)unpipe方法被調(diào)用
 * error            當(dāng)發(fā)生錯(cuò)誤
 *
 * 寫入數(shù)據(jù)方法:
 * write            用于寫入數(shù)據(jù)
 * end              結(jié)束寫入赏陵,之后再寫入會(huì)報(bào)錯(cuò);
 */

fs.createReadStream(path, [options])

創(chuàng)建讀取流

/**
 * path 文件路徑
 * [options] flags:指定文件操作蝙搔,默認(rèn)'r',讀操作;encoding,指定讀取流編碼能颁;autoClose, 是否讀取完成后自動(dòng)關(guān)閉杂瘸,默認(rèn)true;start指定文件開始讀取位置伙菊;end指定文件開始讀結(jié)束位置
 */

var rs = fs.createReadStream(__dirname + '/test.txt', {start: 0, end: 2});
  //open是ReadStream對(duì)象中表示文件打開時(shí)事件败玉,
rs.on('open', function (fd) {
  console.log('開始讀取文件');
});

rs.on('data', function (data) {
  console.log(data.toString());
});

rs.on('end', function () {
  console.log('讀取文件結(jié)束')
});
rs.on('close', function () {
  console.log('文件關(guān)閉');
});

rs.on('error', function (err) {
  console.error(err);
});

//暫停和回復(fù)文件讀取镜硕;
rs.on('open', function () {
  console.log('開始讀取文件');
});

rs.pause();

rs.on('data', function (data) {
  console.log(data.toString());
});

setTimeout(function () {
  rs.resume();
}, 2000);

fs.createWriteStream(path, [options])

創(chuàng)建寫入流

/**
 * path 文件路徑
 * [options] flags:指定文件操作运翼,默認(rèn)'w',;encoding,指定讀取流編碼兴枯;start指定寫入文件的位置
 */

/* ws.write(chunk, [encoding], [callback]);
 * chunk,  可以為Buffer對(duì)象或一個(gè)字符串血淌,要寫入的數(shù)據(jù)
 * [encoding],  編碼
 * [callback],  寫入后回調(diào)
 */

/* ws.end([chunk], [encoding], [callback]);
 * [chunk],  要寫入的數(shù)據(jù)
 * [encoding],  編碼
 * [callback],  寫入后回調(diào)
 */

var ws = fs.createWriteStream(__dirname + '/test.txt', {start: 0});
var buffer = new Buffer('我也喜歡你');
ws.write(buffer, 'utf8', function (err, buffer) {
  console.log(arguments);
  console.log('寫入完成,回調(diào)函數(shù)沒有參數(shù)')
});
//最后再寫入的內(nèi)容
ws.end('再見');
//使用流完成復(fù)制文件操作
var rs = fs.createReadStream(__dirname + '/test.txt')
var ws = fs.createWriteStream(__dirname + '/test/test.txt');

rs.on('data', function (data) {
  ws.write(data)
});

ws.on('open', function (fd) {
  console.log('要寫入的數(shù)據(jù)文件已經(jīng)打開财剖,文件描述符是: ' + fd);
});

rs.on('end', function () {
  console.log('文件讀取完成');
  ws.end('完成', function () {
    console.log('文件全部寫入完成')
  });
});


//關(guān)于WriteStream對(duì)象的write方法返回一個(gè)布爾類型悠夯,當(dāng)緩存區(qū)中數(shù)據(jù)全部寫滿時(shí),返回false;
//表示緩存區(qū)寫滿躺坟,并將立即輸出到目標(biāo)對(duì)象中

//第一個(gè)例子
var ws = fs.createWriteStream(__dirname + '/test/test.txt');
for (var i = 0; i < 10000; i++) {
  var w_flag = ws.write(i.toString());
  //當(dāng)緩存區(qū)寫滿時(shí)沦补,輸出false
  console.log(w_flag);
}


//第二個(gè)例子
var ws = fs.createWriteStream(__dirname + '/test/untiyou.mp3');
var rs = fs.createReadStream(__dirname + '/test/Until You.mp3');
rs.on('data', function (data) {
  var flag = ws.write(data);
  console.log(flag);
});

//系統(tǒng)緩存區(qū)數(shù)據(jù)已經(jīng)全部輸出觸發(fā)drain事件
ws.on('drain', function () {
  console.log('系統(tǒng)緩存區(qū)數(shù)據(jù)已經(jīng)全部輸出。')
});

rs.pipe(destination, [options]);

管道pipe實(shí)現(xiàn)流讀寫

//rs.pipe(destination, [options]);
/**
 * destination 必須一個(gè)可寫入流數(shù)據(jù)對(duì)象
 * [opations] end 默認(rèn)為true咪橙,表示讀取完成立即關(guān)閉文件夕膀;
 */

var rs = fs.createReadStream(__dirname + '/test/Until You.mp3');
var ws = fs.createWriteStream(__dirname + '/test/untiyou.mp3');
rs.pipe(ws);
rs.on('data', function (data) {
  console.log('數(shù)據(jù)可讀')
});
rs.on('end', function () {
  console.log('文件讀取完成');
  //ws.end('再見')
});

推薦文章

fs模塊實(shí)例參考

url 處理模塊

引用

var url = require("url");

URL 的組成介紹

對(duì)于一個(gè) URL 字符串,其組成部分會(huì)有所有不同美侦,其中有些部分只有在URL字符串中存在時(shí)产舞,對(duì)應(yīng)字段才會(huì)出現(xiàn)在解析后對(duì)象中。以下是一個(gè) URL 例子:

http://user:pass@host.com:8080/p/a/t/h?query=string#hash

href: 解析前的完整原始 URL菠剩,協(xié)議名和主機(jī)名已轉(zhuǎn)為小寫
例如: 'http://user:pass@host.com:8080/p/a/t/h?query=string#hash'

protocol: 請(qǐng)求協(xié)議易猫,小寫
例如: 'http:'

slashes: 協(xié)議的“:”號(hào)后是否有“/”
例如: true or false

auth: URL中的認(rèn)證信息
例如: 'user:pass'

host: URL主機(jī)名,包括端口信息具壮,小寫
例如: 'host.com:8080'

hostname: 主機(jī)名擦囊,小寫
例如: 'host.com'

port: 主機(jī)的端口號(hào)
例如: '8080'

path: pathname 和 search的合集
例如: '/p/a/t/h?query=string'

pathname: URL中路徑
例如: '/p/a/t/h'

search: 查詢對(duì)象违霞,即:queryString,包括之前的問(wèn)號(hào)“?”
例如: '?query=string'

query: 查詢字符串中的參數(shù)部分(問(wèn)號(hào)后面部分字符串)瞬场,或者使用 querystring.parse() 解析后返回的對(duì)象
例如: 'query=string' or {'query':'string'}

hash: 錨點(diǎn)部分(即:“#”及其后的部分)
例如: '#hash'

url.parse(urlStr[, parseQueryString][, slashesDenoteHost])

將URL字符串轉(zhuǎn)換為JSON對(duì)象

var urlString = 'http://user:pass@host.com:8080/p/a/t/h?query=string#hash';
var result = url.parse(urlString);
console.log(result);

//輸出結(jié)果如下
{ protocol: 'http:',
  slashes: true,
  auth: 'user:pass',
  host: 'host.com:8080',
  port: '8080',
  hostname: 'host.com',
  hash: '#hash',
  search: '?query=string',
  query: 'query=string',
  pathname: '/p/a/t/h',
  path: '/p/a/t/h?query=string',
  href: 'http://user:pass@host.com:8080/p/a/t/h?query=string#hash' 
}

//第二個(gè)可選參數(shù)設(shè)置為true時(shí)买鸽,會(huì)使用querystring模塊來(lái)解析URL中德查詢字符串部分,默認(rèn)為 false贯被。
var result1 = url.parse(urlString, true);
console.log(result1);
//輸出結(jié)果如下
{ protocol: 'http:',
  slashes: true,
  auth: 'user:pass',
  host: 'host.com:8080',
  port: '8080',
  hostname: 'host.com',
  hash: '#hash',
  search: '?query=string',
  query: {query:"string"},
  pathname: '/p/a/t/h',
  path: '/p/a/t/h?query=string',
  href: 'http://user:pass@host.com:8080/p/a/t/h?query=string#hash' 
}

url.format(urlObj)

用于格式化URL對(duì)象眼五。輸入一個(gè) URL 對(duì)象,返回格式化后的 URL 字符串彤灶。示例如下

var urlObj = { 
  protocol: 'http:',
    slashes: true,
    hostname: 'jianshu.com',
    port: 80,
    hash: '#hash',
    search: '?query=string',
    path: '/nodejs?query=string'
}
var result = url.format(urlObj);
console.log(result);
//輸出結(jié)果如下
http://jianshu.com:80?query=string#hash
/*
*傳入的URL對(duì)象會(huì)做以下處理:
*
*href 屬性會(huì)被忽略
*protocol無(wú)論是否有末尾的 : (冒號(hào))看幼,會(huì)同樣的處理
**這些協(xié)議包括 http, https, ftp, gopher, file 后綴是 :// (冒號(hào)-斜杠-斜杠).
**所有其他的協(xié)議如 mailto, xmpp, aim, sftp, foo, 等 會(huì)加上后綴 : (冒號(hào))
*auth 如果有將會(huì)出現(xiàn).
*host 優(yōu)先使用,將會(huì)替代 hostname 和port
*hostname 如果 host 屬性沒被定義幌陕,則會(huì)使用此屬性.
*port 如果 host 屬性沒被定義诵姜,則會(huì)使用此屬性.
*pathname 將會(huì)同樣處理無(wú)論結(jié)尾是否有/ (斜杠)
*search 將會(huì)替代 query屬性
*query (object類型; 詳細(xì)請(qǐng)看 querystring) 如果沒有 search,將會(huì)使用此屬性.
*search 無(wú)論前面是否有 ? (問(wèn)號(hào)),都會(huì)同樣的處理
*hash無(wú)論前面是否有# (井號(hào), 錨點(diǎn))搏熄,都會(huì)同樣處理
*/

url.resolve(from, to)

用于拼接路徑

url.resolve('/one/two/three', 'four')         // '/one/two/four'
url.resolve('http://example.com/', '/one')    // 'http://example.com/one'
url.resolve('http://example.com/one', '/two') // 'http://example.com/two'

query string 參數(shù)處理模塊

引用

var querystring = require('querystring')

querystring.stringify(obj, [sep], [eq])

對(duì)象格式化成參數(shù)字符串 ,obj就是要格式化的對(duì)象,必選參數(shù);[sep]指分隔符 默認(rèn)'&'; [eq]指分配符 默認(rèn)'='

var querystring = require('querystring')
var param = {name:"feng",age:"33"};
    
var paramStr1 = querystring.stringify(param);
console.log(paramStr1);//name=feng&age=33
var paramStr2 = querystring.stringify(param,'$','-');
console.log(paramStr2);//name-feng$age-33

本方法會(huì)自動(dòng)編碼漢字

querystring.parse(str, [sep], [eq], [options])

參數(shù)字符串格式化成對(duì)象

var paramStr1 = 'name=feng&age=33';
var paramStr2 = 'name-feng$age-33';

var param1 = querystring.parse(paramStr1);
console.log(param1);//{ name: 'feng', age: '33' }

var param2 = querystring.parse(paramStr2, '$', '-');
console.log(param2);//{ name: 'feng', age: '33' }

querystring.escape

參數(shù)編碼

var param = "name=阿峰&age=33";

console.log(querystring.escape(param));
//name%3D%E9%98%BF%E5%B3%B0%26age%3D33

querystring.unescape

參數(shù)解碼

var param = "name=阿峰&age=33";

console.log(querystring.unescape(querystring.escape(param)));
//name=阿峰&age=33

os

引用

var os = require('os');

常用函數(shù)

//cpu架構(gòu)
os.arch();

//操作系統(tǒng)內(nèi)核
os.type();

//操作系統(tǒng)平臺(tái)
os.platform();

//系統(tǒng)開機(jī)時(shí)間
os.uptime();

//主機(jī)名
os.hostname();

//主目錄
os.homedir();


//內(nèi)存
os.totalmem();//總內(nèi)存
os.freemem();// 空閑內(nèi)存

//cpu
const cpus = os.cpus();
cpus.forEach((cpu,idx,arr)=>{
    var times = cpu.times;
    console.log(`cpu${idx}:`);
    console.log(`型號(hào):${cpu.model}`);
    console.log(`頻率:${cpu.speed}MHz`);
    console.log(`使用率:${((1-times.idle/(times.idle+times.user+times.nice+times.sys+times.irq))*100).toFixed(2)}%`);
});

//網(wǎng)卡
const networksObj = os.networkInterfaces();
for(let nw in networksObj){
    let objArr = networksObj[nw];
    console.log(`\r\n${nw}:`);
    objArr.forEach((obj,idx,arr)=>{
        console.log(`地址:${obj.address}`);
        console.log(`掩碼:${obj.netmask}`);
        console.log(`物理地址:${obj.mac}`);
        console.log(`協(xié)議族:${obj.family}`);
    });
}

stream

為什么使用流

nodejs的fs模塊并沒有提供一個(gè)copy的方法棚唆,但我們可以很容易的實(shí)現(xiàn)一個(gè),比如:

var source = fs.readFileSync('/path/to/source', {encoding: 'utf8'});
fs.writeFileSync('/path/to/dest', source);

上面的這段代碼并沒有什么問(wèn)題心例,但是在每次請(qǐng)求時(shí)宵凌,我們都會(huì)把整個(gè)源文件讀入到內(nèi)存中,然后再把結(jié)果返回給客戶端止后。想想看瞎惫,如果源文件非常大,在響應(yīng)大量用戶的并發(fā)請(qǐng)求時(shí)译株,程序可能會(huì)消耗大量的內(nèi)存瓜喇,這樣很可能會(huì)造成用戶連接緩慢的問(wèn)題。

理想的方法應(yīng)該是讀一部分歉糜,寫一部分乘寒,不管文件有多大,只要時(shí)間允許现恼,總會(huì)處理完成,這里就需要用到流的概念黍檩。

上面的文件復(fù)制可以簡(jiǎn)單實(shí)現(xiàn)一下:

var fs = require('fs');
var readStream = fs.createReadStream('/path/to/source');
var writeStream = fs.createWriteStream('/path/to/dest');

readStream.on('data', function(chunk) { // 當(dāng)有數(shù)據(jù)流出時(shí)叉袍,寫入數(shù)據(jù)
    writeStream.write(chunk);
});

readStream.on('end', function() { // 當(dāng)沒有數(shù)據(jù)時(shí),關(guān)閉數(shù)據(jù)流
    writeStream.end();
});

上面的寫法有一些問(wèn)題刽酱,如果寫入的速度跟不上讀取的速度喳逛,有可能導(dǎo)致數(shù)據(jù)丟失。正常的情況應(yīng)該是棵里,寫完一段润文,再讀取下一段姐呐,如果沒有寫完的話,就讓讀取流先暫停典蝌,等寫完再繼續(xù)曙砂,于是代碼可以修改為:

var fs = require('fs');
var readStream = fs.createReadStream('/path/to/source');
var writeStream = fs.createWriteStream('/path/to/dest');

readStream.on('data', function(chunk) { // 當(dāng)有數(shù)據(jù)流出時(shí),寫入數(shù)據(jù)
    if (writeStream.write(chunk) === false) { // 如果沒有寫完骏掀,暫停讀取流
        readStream.pause();
    }
});

writeStream.on('drain', function() { // 寫完后鸠澈,繼續(xù)讀取
    readStream.resume();
});

readStream.on('end', function() { // 當(dāng)沒有數(shù)據(jù)時(shí),關(guān)閉數(shù)據(jù)流
    writeStream.end();
});

或者使用更直接的pipe

// pipe自動(dòng)調(diào)用了data,end等事件
fs.createReadStream('/path/to/source').pipe(fs.createWriteStream('/path/to/dest'));

下面是一個(gè)完整的復(fù)制文件的過(guò)程

var fs = require('fs'),
    path = require('path'),
    out = process.stdout;

var filePath = 'Users/feng/Documents/something/kobe.gif';

var readStream = fs.createReadStream(filePath);
var writeStream = fs.createWriteStream('file.gif');

var stat = fs.statSync(filePath);

var totalSize = stat.size;
var passedLength = 0;
var lastSize = 0;
var startTime = Date.now();

readStream.on('data', function(chunk) {

    passedLength += chunk.length;

    if (writeStream.write(chunk) === false) {
        readStream.pause();
    }
});

readStream.on('end', function() {
    writeStream.end();
});

writeStream.on('drain', function() {
    readStream.resume();
});

setTimeout(function show() {
    var percent = Math.ceil((passedLength / totalSize) * 100);
    var size = Math.ceil(passedLength / 1000000);
    var diff = size - lastSize;
    lastSize = size;
    out.clearLine();
    out.cursorTo(0);
    out.write('已完成' + size + 'MB, ' + percent + '%, 速度:' + diff + 'MB/s');
    if (passedLength < totalSize) {
        setTimeout(show, 500);
    } else {
        var endTime = Date.now();
        console.log();
        console.log('共用時(shí):' + (endTime - startTime) / 1000 + '秒截驮。');
    }
}, 500);

Readable流

fs.createReadStream(path[, options])用來(lái)打開一個(gè)可讀的文件流笑陈,它返回一個(gè)fs.ReadStream對(duì)象。path參數(shù)指定文件的路徑葵袭,可選的options是一個(gè)JS對(duì)象涵妥,可以指定一些選項(xiàng),類似下面這樣:

{ flags: 'r',
  encoding: 'utf8',
  fd: null,
  mode: 0666,
  autoClose: true
}

options的flags屬性指定用什么模式打開文件:

  • ’w’代表寫坡锡,’r’代表讀蓬网,類似的還有’r+’、’w+’娜氏、’a’等拳缠,與Linux下的open函數(shù)接受的讀寫模式類似。
  • encoding指定打開文件時(shí)使用編碼格式贸弥,默認(rèn)就是“utf8”窟坐,你還可以為它指定”ascii”或”base64”。
  • fd屬性默認(rèn)為null绵疲,當(dāng)你指定了這個(gè)屬性時(shí)哲鸳,createReadableStream會(huì)根據(jù)傳入的fd創(chuàng)建一個(gè)流,忽略path盔憨。另外你要是想讀取一個(gè)文件的特定區(qū)域徙菠,可以配置start、end屬性郁岩,指定起始和結(jié)束(包含在內(nèi))的字節(jié)偏移婿奔。
  • autoClose屬性為true(默認(rèn)行為)時(shí),當(dāng)發(fā)生錯(cuò)誤或文件讀取結(jié)束時(shí)會(huì)自動(dòng)關(guān)閉文件描述符问慎。

Readable還提供了一些函數(shù)萍摊,我們可以用它們讀取或操作流:

  • read([size]):如果你給read方法傳遞了一個(gè)制定大小作為參數(shù),那它會(huì)返回指定數(shù)量的數(shù)據(jù)如叼,如果數(shù)據(jù)不足冰木,就會(huì)返回null。如果你不給read方法傳參,它會(huì)返回內(nèi)部緩沖區(qū)里的所有數(shù)據(jù)踊沸,如果沒有數(shù)據(jù)歇终,會(huì)返回null,此時(shí)有可能說(shuō)明遇到了文件末尾逼龟。read返回的數(shù)據(jù)可能是Buffer對(duì)象评凝,也可能是String對(duì)象。
  • setEncoding(encoding):給流設(shè)置一個(gè)編碼格式审轮,用于解碼讀到的數(shù)據(jù)肥哎。調(diào)用此方法后睛蛛,read([size])方法返回String對(duì)象镜撩。
  • pause():暫筒腔辏可讀流痊夭,不再發(fā)出data事件
  • resume():恢復(fù)可讀流智亮,繼續(xù)發(fā)出data事件
  • pipe(destination,[options]):把這個(gè)可讀流的輸出傳遞給destination指定的Writable流克懊,兩個(gè)流組成一個(gè)管道洋丐。options是一個(gè)JS對(duì)象豪诲,這個(gè)對(duì)象有一個(gè)布爾類型的end屬性吊圾,默認(rèn)值為true达椰,當(dāng)end為true時(shí),Readable結(jié)束時(shí)自動(dòng)結(jié)束Writable项乒。注意啰劲,我們可以把一個(gè)Readable與若干Writable連在一起,組成多個(gè)管道檀何,每一個(gè)Writable都能得到同樣的數(shù)據(jù)蝇裤。這個(gè)方法返回destination,如果destination本身又是Readable流频鉴,就可以級(jí)聯(lián)調(diào)用pipe(比如我們?cè)谑褂胓zip壓縮栓辜、解壓縮時(shí)就會(huì)這樣,馬上會(huì)講到)垛孔。
  • unpipe([destination]):端口與指定destination的管道藕甩。不傳遞destination時(shí),斷開與這個(gè)可讀流連在一起的所有管道周荐。

Readable流提供了以下事件:

  • readable:在數(shù)據(jù)塊可以從流中讀取的時(shí)候發(fā)出狭莱。它對(duì)應(yīng)的處理器沒有參數(shù),可以在處理器里調(diào)用read([size])方法讀取數(shù)據(jù)概作。
  • data:有數(shù)據(jù)可讀時(shí)發(fā)出腋妙。它對(duì)應(yīng)的處理器有一個(gè)參數(shù),代表數(shù)據(jù)仆嗦。如果你只想快快地讀取一個(gè)流的數(shù)據(jù)辉阶,給data關(guān)聯(lián)一個(gè)處理器是最方便的辦法先壕。處理器的參數(shù)是Buffer對(duì)象瘩扼,如果你調(diào)用了Readable的setEncoding(encoding)方法谆甜,處理器的參數(shù)就是String對(duì)象。
  • end:當(dāng)數(shù)據(jù)被讀完時(shí)發(fā)出集绰。對(duì)應(yīng)的處理器沒有參數(shù)规辱。
  • close:當(dāng)?shù)讓拥馁Y源,如文件栽燕,已關(guān)閉時(shí)發(fā)出罕袋。不是所有的Readable流都會(huì)發(fā)出這個(gè)事件。對(duì)應(yīng)的處理器沒有參數(shù)碍岔。
  • error:當(dāng)在接收數(shù)據(jù)中出現(xiàn)錯(cuò)誤時(shí)發(fā)出浴讯。對(duì)應(yīng)的處理器參數(shù)是Error的實(shí)例,它的message屬性描述了錯(cuò)誤原因蔼啦,stack屬性保存了發(fā)生錯(cuò)誤時(shí)的堆棧信息榆纽。

一個(gè)基本的可讀流實(shí)例

var fs = require('fs');

var readable = fs.createReadStream('text.js',{
  flags: 'r',
  encoding: 'utf8',
  autoClose: true,
  mode: 0666,
});

readable.on('open', function(fd){
  console.log('file was opened, fd - ', fd);
});

readable.on('readable', function(){
  console.log('received readable');
});

readable.on('data', function(chunk){
  console.log('read %d bytes: %s', chunk.length, chunk);
});

readable.on('end', function(){
  console.log('read end');
});

readable.on('close', function(){
  console.log('file was closed.');
});

readable.on('error', function(err){
  console.log('error occured: %s', err.message);
});

Writable

Writable流提供了一個(gè)接口,用來(lái)把數(shù)據(jù)寫入到目的設(shè)備(或內(nèi)存)中捏肢。

Writable提供了一些函數(shù)來(lái)操作流

  • write(chunk[,encoding][,callback])可以把數(shù)據(jù)寫入流中奈籽。其中,chunk是待寫入的數(shù)據(jù)鸵赫,是Buffer或String對(duì)象衣屏。這個(gè)參數(shù)是必須的,其它參數(shù)都是可選的辩棒。如果chunk是String對(duì)象狼忱,encoding可以用來(lái)指定字符串的編碼格式,write會(huì)根據(jù)編碼格式將chunk解碼成字節(jié)流再來(lái)寫入盗温。callback是數(shù)據(jù)完全刷新到流中時(shí)會(huì)執(zhí)行的回調(diào)函數(shù)藕赞。write方法返回布爾值,當(dāng)數(shù)據(jù)被完全處理后返回true(不一定是完全寫入設(shè)備哦)卖局。

  • end([chunk] [,encoding][,callback])方法可以用來(lái)結(jié)束一個(gè)可寫流斧蜕。它的三個(gè)參數(shù)都是可選的。chunk和encoding的含義與write方法類似砚偶。callback是一個(gè)可選的回調(diào)批销,當(dāng)你提供它時(shí),它會(huì)被關(guān)聯(lián)到Writable的finish事件上染坯,這樣當(dāng)finish事件發(fā)射時(shí)它就會(huì)被調(diào)用均芽。

現(xiàn)在我們來(lái)看看Writable公開的事件:

finish: 在end()被調(diào)用、所有數(shù)據(jù)都已被寫入底層設(shè)備后發(fā)射单鹿。對(duì)應(yīng)的處理器函數(shù)沒有參數(shù)掀宋。
pipe: 當(dāng)你在Readable流上調(diào)用pipe()方法時(shí),Writable流會(huì)發(fā)射這個(gè)事件,對(duì)應(yīng)的處理器函數(shù)有一個(gè)參數(shù)劲妙,類型是Readable湃鹊,指向與它連接的那個(gè)Readable流。
unpipe: 當(dāng)你在Readable流上調(diào)用unpipe()方法時(shí)镣奋,Writable流會(huì)發(fā)射這個(gè)事件币呵,對(duì)應(yīng)的處理器函數(shù)有一個(gè)參數(shù),類型是Readable侨颈,指向與剛與它斷開連接的那個(gè)Readable流余赢。
error: 出錯(cuò)時(shí)發(fā)射,對(duì)應(yīng)的處理器函數(shù)的參數(shù)是Error對(duì)象哈垢。

看一個(gè)簡(jiǎn)單實(shí)例

var fs = require('fs');

var writable = fs.createWriteStream('example.txt',{
  flags: 'w',
  defaultEncoding: 'utf8',
  mode: 0666,
});

writable.on('finish', function(){
  console.log('write finished');
  process.exit(0);
});

writable.on('error', function(err){
  console.log('write error - %s', err.message);
});

writable.write('hello 地方', 'utf8');

writable.end();

其他流

參考文章

nodejs stream手冊(cè)

其他系列文章

Nodejs模塊學(xué)習(xí)筆記
極客學(xué)院 nodejs官方文檔

未完待續(xù)~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末妻柒,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子耘分,更是在濱河造成了極大的恐慌蛤奢,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件陶贼,死亡現(xiàn)場(chǎng)離奇詭異啤贩,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)拜秧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門痹屹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人枉氮,你說(shuō)我怎么就攤上這事志衍。” “怎么了聊替?”我有些...
    開封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵楼肪,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我惹悄,道長(zhǎng)春叫,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任泣港,我火速辦了婚禮暂殖,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘当纱。我一直安慰自己呛每,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開白布坡氯。 她就那樣靜靜地躺著晨横,像睡著了一般洋腮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上手形,一...
    開封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天徐矩,我揣著相機(jī)與錄音,去河邊找鬼叁幢。 笑死,一個(gè)胖子當(dāng)著我的面吹牛坪稽,可吹牛的內(nèi)容都是我干的曼玩。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼窒百,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼黍判!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起篙梢,我...
    開封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤顷帖,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后渤滞,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體贬墩,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年妄呕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了陶舞。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡绪励,死狀恐怖肿孵,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情疏魏,我是刑警寧澤停做,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站大莫,受9級(jí)特大地震影響蛉腌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜只厘,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一眉抬、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧懈凹,春花似錦蜀变、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)爬舰。三九已至,卻和暖如春寒瓦,著一層夾襖步出監(jiān)牢的瞬間情屹,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工杂腰, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留垃你,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓喂很,卻偏偏與公主長(zhǎng)得像惜颇,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子少辣,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

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

  • https://nodejs.org/api/documentation.html 工具模塊 Assert 測(cè)試 ...
    KeKeMars閱讀 6,305評(píng)論 0 6
  • 文件系統(tǒng)模塊是一個(gè)封裝了標(biāo)準(zhǔn)的 POSIX 文件 I/O 操作的集合凌摄。通過(guò)require('fs')使用這個(gè)模塊。...
    保川閱讀 773評(píng)論 0 0
  • Node.js是目前非忱焖В火熱的技術(shù)锨亏,但是它的誕生經(jīng)歷卻很奇特。 眾所周知忙干,在Netscape設(shè)計(jì)出JavaScri...
    w_zhuan閱讀 3,607評(píng)論 2 41
  • 模塊 名詞解釋:每一個(gè)js文件就是一個(gè)模塊器予,而文件路徑就是模塊名。每個(gè)模塊(也就是每個(gè)js文件)都有requir,...
    親愛的孟良閱讀 514評(píng)論 0 0
  • 雖然時(shí)不時(shí)會(huì)有點(diǎn)小思念末融,但是內(nèi)心平靜而感恩了 我刪了過(guò)去一年喜歡的人 我可是說(shuō),在大理的那段時(shí)間暇韧,接受了孤獨(dú)勾习,所以...
    helen1990_閱讀 116評(píng)論 0 0