天天看點

NodeJs 的fs子產品對檔案的操作Node.js之fs用法詳解

<1>js 裡面的fs子產品

  • readFile(),readFileSync()
  • writeFile(),writeFileSync()
  • exists(path, callback)
  • mkdir(),writeFile(),readFile()
  • mkdirSync(),writeFileSync(),readFileSync()
  • readdir(),readdirSync()
  • stat()
  • watchfile(),unwatchfile()
  • createReadStream()
  • createWriteStream()

重要說明:本教程已經搬遷,此處不再維護,請通路新網址:wangdoc.com/javascript。

fs

filesystem

的縮寫,該子產品提供本地檔案的讀寫能力,基本上是POSIX檔案操作指令的簡單包裝。但是,這個子產品幾乎對所有操作提供異步和同步兩種操作方式,供開發者選擇。

readFile(),readFileSync()

readFile

方法用于異步讀取資料。

fs.readFile('./image.png', function (err, buffer) {
  if (err) throw err;
  process(buffer);
});
           

readFile

方法的第一個參數是檔案的路徑,可以是絕對路徑,也可以是相對路徑。注意,如果是相對路徑,是相對于目前程序所在的路徑(

process.cwd()

),而不是相對于目前腳本所在的路徑。

readFile

方法的第二個參數是讀取完成後的回調函數。該函數的第一個參數是發生錯誤時的錯誤對象,第二個參數是代表檔案内容的

Buffer

執行個體。

readFileSync

方法用于同步讀取檔案,傳回一個字元串。

var text = fs.readFileSync(fileName, 'utf8');

// 将檔案按行拆成數組
text.split(/\r?\n/).forEach(function (line) {
  // ...
});
           

readFileSync

方法的第一個參數是檔案路徑,第二個參數可以是一個表示配置的對象,也可以是一個表示文本檔案編碼的字元串。預設的配置對象是

{ encoding: null, flag: 'r' }

,即檔案編碼預設為

null

,讀取模式預設為

r

(隻讀)。如果第二個參數不指定編碼(

encoding

),

readFileSync

方法傳回一個

Buffer

執行個體,否則傳回的是一個字元串。

不同系統的行結尾字元不同,可以用下面的方法判斷。

// 方法一,查詢現有的行結尾字元
var EOL =
  fileContents.indexOf('\r\n') >= 0 ? '\r\n' : '\n';

// 方法二,根據目前系統處理
var EOL =
  (process.platform === 'win32' ? '\r\n' : '\n');
           

writeFile(),writeFileSync()

writeFile

方法用于異步寫入檔案。

fs.writeFile('message.txt', 'Hello Node.js', (err) => {
  if (err) throw err;
  console.log('It\'s saved!');
});
           

上面代碼中,

writeFile

方法的第一個參數是寫入的檔案名,第二個參數是寫入的字元串,第三個參數是回調函數。

回調函數前面,還可以再加一個參數,表示寫入字元串的編碼(預設是

utf8

)。

fs.writeFile('message.txt', 'Hello Node.js', 'utf8', callback);
           

writeFileSync

方法用于同步寫入檔案。

fs.writeFileSync(fileName, str, 'utf8');
           

它的第一個參數是檔案路徑,第二個參數是寫入檔案的字元串,第三個參數是檔案編碼,預設為utf8。

exists(path, callback)

exists方法用來判斷給定路徑是否存在,然後不管結果如何,都會調用回調函數。

fs.exists('/path/to/file', function (exists) {
  util.debug(exists ? "it's there" : "no file!");
});
           

上面代碼表明,回調函數的參數是一個表示檔案是否存在的布爾值。

需要注意的是,不要在

open

方法之前調用

exists

方法,open方法本身就能檢查檔案是否存在。

下面的例子是如果給定目錄存在,就删除它。

if (fs.existsSync(outputFolder)) {
  console.log('Removing ' + outputFolder);
  fs.rmdirSync(outputFolder);
}
           

mkdir(),writeFile(),readFile()

mkdir方法用于建立目錄。

var fs = require('fs');

fs.mkdir('./helloDir',0777, function (err) {
  if (err) throw err;
});

           

mkdir接受三個參數,第一個是目錄名,第二個是權限值,第三個是回調函數。

writeFile方法用于寫入檔案。

var fs = require('fs');

fs.writeFile('./helloDir/message.txt', 'Hello Node', function (err) {
  if (err) throw err;
  console.log('檔案寫入成功');
});

           

readFile方法用于讀取檔案内容。

var fs = require('fs');

fs.readFile('./helloDir/message.txt','UTF-8' ,function (err, data) {
  if (err) throw err;
  console.log(data);
});
           

上面代碼使用readFile方法讀取檔案。readFile方法的第一個參數是檔案名,第二個參數是檔案編碼,第三個參數是回調函數。可用的檔案編碼包括“ascii”、“utf8”和“base64”。如果沒有指定檔案編碼,傳回的是原始的緩存二進制資料,這時需要調用buffer對象的toString方法,将其轉為字元串。

var fs = require('fs');
fs.readFile('example_log.txt', function (err, logData) {
  if (err) throw err;
  var text = logData.toString();
});

           

readFile方法是異步操作,是以必須小心,不要同時發起多個readFile請求。

for(var i = 1; i <= 1000; i++) {
  fs.readFile('./'+i+'.txt', function() {
     // do something with the file
  });
}
           

上面代碼會同時發起1000個readFile異步請求,很快就會耗盡系統資源。

mkdirSync(),writeFileSync(),readFileSync()

這三個方法是建立目錄、寫入檔案、讀取檔案的同步版本。

fs.mkdirSync('./helloDirSync',0777);
fs.writeFileSync('./helloDirSync/message.txt', 'Hello Node');
var data = fs.readFileSync('./helloDirSync/message.txt','UTF-8');
console.log('file created with contents:');
console.log(data);
           

對于流量較大的伺服器,最好還是采用異步操作,因為同步操作時,隻有前一個操作結束,才會開始後一個操作,如果某個操作特别耗時(常常發生在讀寫資料時),會導緻整個程式停頓。

readdir(),readdirSync()

readdir

方法用于讀取目錄,傳回一個所包含的檔案和子目錄的數組。

fs.readdir(process.cwd(), function (err, files) {
  if (err) {
    console.log(err);
    return;
  }

  var count = files.length;
  var results = {};
  files.forEach(function (filename) {
    fs.readFile(filename, function (data) {
      results[filename] = data;
      count--;
      if (count <= 0) {
        // 對所有檔案進行處理
      }
    });
  });
});
           

readdirSync

方法是

readdir

方法的同步版本。下面是同步列出目錄内容的代碼。

var files = fs.readdirSync(dir);
files.forEach(function (filename) {
  var fullname = path.join(dir,filename);
  var stats = fs.statSync(fullname);
  if (stats.isDirectory()) filename += '/';
  process.stdout.write(filename + '\t' +
    stats.size + '\t' +
    stats.mtime + '\n'
  );
});
           

stat()

stat方法的參數是一個檔案或目錄,它産生一個對象,該對象包含了該檔案或目錄的具體資訊。我們往往通過該方法,判斷正在處理的到底是一個檔案,還是一個目錄。

var fs = require('fs');

fs.readdir('/etc/', function (err, files) {
  if (err) throw err;

  files.forEach( function (file) {
    fs.stat('/etc/' + file, function (err, stats) {
      if (err) throw err;

      if (stats.isFile()) {
        console.log("%s is file", file);
      }
      else if (stats.isDirectory ()) {
      console.log("%s is a directory", file);
      }
    console.log('stats:  %s',JSON.stringify(stats));
    });
  });
});
           

watchfile(),unwatchfile()

watchfile方法監聽一個檔案,如果該檔案發生變化,就會自動觸發回調函數。

var fs = require('fs');

fs.watchFile('./testFile.txt', function (curr, prev) {
  console.log('the current mtime is: ' + curr.mtime);
  console.log('the previous mtime was: ' + prev.mtime);
});

fs.writeFile('./testFile.txt', "changed", function (err) {
  if (err) throw err;

  console.log("file write complete");   
});
           

unwatchfile

方法用于解除對檔案的監聽。

createReadStream()

createReadStream

方法往往用于打開大型的文本檔案,建立一個讀取操作的資料流。所謂大型文本檔案,指的是文本檔案的體積很大,讀取操作的緩存裝不下,隻能分成幾次發送,每次發送會觸發一個

data

事件,發送結束會觸發

end

事件。

var fs = require('fs');

function readLines(input, func) {
  var remaining = '';

  input.on('data', function(data) {
    remaining += data;
    var index = remaining.indexOf('\n');
    var last  = 0;
    while (index > -1) {
      var line = remaining.substring(last, index);
      last = index + 1;
      func(line);
      index = remaining.indexOf('\n', last);
    }

    remaining = remaining.substring(last);
  });

  input.on('end', function() {
    if (remaining.length > 0) {
      func(remaining);
    }
  });
}

function func(data) {
  console.log('Line: ' + data);
}

var input = fs.createReadStream('lines.txt');
readLines(input, func);
           

createWriteStream()

createWriteStream

方法建立一個寫入資料流對象,該對象的

write

方法用于寫入資料,

end

方法用于結束寫入操作。

var out = fs.createWriteStream(fileName, {
  encoding: 'utf8'
});
out.write(str);
out.end();
           

createWriteStream

方法和

createReadStream

方法配合,可以實作拷貝大型檔案。

function fileCopy(filename1, filename2, done) {
  var input = fs.createReadStream(filename1);
  var output = fs.createWriteStream(filename2);

  input.on('data', function(d) { output.write(d); });
  input.on('error', function(err) { throw err; });
  input.on('end', function() {
    output.end();
    if (done) done();
  });
}
           

<2>NodeJs 裡面的fs子產品

Node.js 提供一組類似 UNIX(POSIX)标準的檔案操作API。 Node 導入檔案系統子產品(fs)文法如下所示:

var fs = require("fs")
           

對檔案的操作

檔案讀取

Node.js 檔案系統(fs 子產品)子產品中的方法均有異步和同步版本,例如讀取檔案内容的函數有異步的 fs.readFile() 和同步的 fs.readFileSync()。

異步的方法函數最後一個參數為回調函數,回調函數的第一個參數包含了錯誤資訊(error)。

建議大家使用異步方法,比起同步,異步方法性能更高,速度更快,而且沒有阻塞。

同步

//同步 所有同步的函數都是函數後面加Sync;
 var res = fs.writeFileSync("1.txt","我是寫入内容");
           

異步

//檔案的讀取
fs.readFile("1.txt",function (err,data) {
     if (err){
        console.log(err)
     }else {
         console.log(data.toString())
     }
 })
           

檔案寫入

//建立: writeFile(檔案名,寫入内容,配置參數,回調函數) 異步
//配置參數:
/*
* a :追加
* w :寫入
* r :讀取
* */
fs.writeFile("2.txt","我是2.txt",{flag:"a"},function (err) {
    if(err){
        console.log(err);
    }else {
        console.log("寫入成功");
    }
})

// 追加寫入
fs.appendFile("2.txt","我是追加的字元",function (err) {
    if(err){
      return  console.log(err);
    }else {
        console.log("追加成功");
    }
})
           

檔案名修改

//檔案修改(檔案名的修改) 
 fs.rename("5.txt","1.txt",function (err) {
     if(err){
         console.log(err)
     }else {
         console.log("修改成功");
     }
 })
           
//檔案删除
 fs.unlink("2.txt",function (err) {
     if(err){
         return console.log(err)
     }else {
         console.log("删除成功")
     }
 })
           

由于fs子產品中沒有對檔案的複制,我們可以自己通過以上操作封裝一個

function mycopy(file1,file2) {
    //讀取檔案
    var res = fs.readFile(file1,function (err,data) {
        if (err){
            console.log(err)
        }else {
            var res = data.toString()
            fs.writeFile(file2,res,function (err) {
                if(err){
                    console.log(err)
                }else {
                    console.log("寫入成功");
                }
            })
        }
    })
}

//
function mycopy(src,dest) {
    fs.writeFileSync(dest,fs.readFileSync(src));
}
           

對檔案夾的操作

檔案夾的操作/目錄操作

// 目錄建立:
// 1、1:執行-x 2、2:寫-w 3、4:讀-r
fs.mkdir("10",0666,function (err) {
    if(err){
        console.log(err)
    }else {
        console.log("建立成功");
    }
})
           

修改檔案夾權限

fs.chmod("11",0777,function (err) {
    if(err){
        console.log(err)
    }else {
        console.log("修改權限成功")
    }
})
           

判斷檔案或者檔案夾是否存在

fs.exists("10",function (exists) {
    if(exists){
        console.log("檔案夾已存在");
    }else {
        fs.mkdir("10",0777,function (err) {
            if(err){
                return console.log(err);
            }else{
                console.log("建立成功");
            }
        })
    }
})
           

删除檔案夾 : 隻能删除空檔案夾

fs.rmdir("10",function (err) {
    if(err){
        return console.log(err)
    }else {
        console.log("删除成功");
    }
})
           

讀取檔案夾

fs.readdir("10",function (err,data) {
    if(err){
        console.log(err);
    }else {
        console.log(data);
    }
})
           

顯示檔案的詳細資訊:

//針對詳細資訊來判斷是否是檔案
fs.stat("10",function (err,data) {
    if(err){
       return console.log(err)
    }else {
        // console.log(data);
        //判斷是否是檔案
        var res = data.isFile();
        //判斷是否是檔案夾
        // data.isDirectory();
        if(res){
            console.log("是檔案")
        }else {
            console.log("是檔案夾")
        }
    }
})
           

由于node.js中沒有删除包含檔案的檔案夾的函數,是以我們仿寫一個函數來删除包含檔案的檔案的函數

// 删除檔案夾的函數 同步
var removeDir = function(src) {
   // 擷取到檔案夾裡的内容
   var arr = fs.readdirSync(src);
   //判斷是否是檔案,如果是檔案就删除;如果是檔案夾再執行相同過程
    for(var i=0;i<arr.length;i++){
       //子檔案的詳細資訊
       // 組裝檔案或者檔案夾的路徑
        var url = src+"/"+arr[i];
        var data = fs.statSync(url);
        //判斷每個元素是檔案或者是檔案夾
        if(data.isFile()){
            //是檔案
            fs.unlinkSync(url);
        }else {
            //是檔案夾
            removeDir(url)
        }
    }
    //删除空檔案夾
    fs.rmdirSync(src);
}
           

<3>Node.js檔案系統的API fs子產品

Node.js的檔案系統的Api

//公共引用
var fs = require('fs'),
path = require('path');
           

1、讀取檔案readFile函數

//readFile(filename,[options],callback);

/**
 * filename, 必選參數,檔案名
 * [options],可選參數,可指定flag(檔案操作選項,如r+ 讀寫;w+ 讀寫,檔案不存在則建立)及encoding屬性
 * callback 讀取檔案後的回調函數,參數預設第一個err,第二個data 資料
 */

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

2、寫檔案

// fs.writeFile(filename,data,[options],callback);
var w_data = '這是一段通過fs.writeFile函數寫入的内容;\r\n';
var w_data = new Buffer(w_data);

/**
 * filename, 必選參數,檔案名
 * data, 寫入的資料,可以字元或一個Buffer對象
 * [options],flag,mode(權限),encoding
 * callback 讀取檔案後的回調函數,參數預設第一個err,第二個data 資料
 */

fs.writeFile(__dirname + '/test.txt', w_data, {flag: 'a'}, function (err) {
   if(err) {
    console.error(err);
    } else {
       console.log('寫入成功');
    }
});
           

3、以追加方式寫檔案

// fs.appendFile(filename,data,[options],callback);

fs.appendFile(__dirname + '/test.txt', '使用fs.appendFile追加檔案内容', function () {
  console.log('追加内容完成');
});
           

4、打開檔案

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

/**
 * filename, 必選參數,檔案名
 * flags, 操作辨別,如"r",讀方式打開
 * [mode],權限,如777,表示任何使用者讀寫可執行
 * callback 打開檔案後回調函數,參數預設第一個err,第二個fd為一個整數,表示打開檔案傳回的檔案描述符,window中又稱檔案句柄
 */

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

5、讀檔案,讀取打開的檔案内容到緩沖區中;

//fs.read(fd, buffer, offset, length, position, callback);
/**
 * fd, 使用fs.open打開成功後傳回的檔案描述符
 * buffer, 一個Buffer對象,v8引擎配置設定的一段記憶體
 * offset, 整數,向緩存區中寫入時的初始位置,以位元組為機關
 * length, 整數,讀取檔案的長度
 * position, 整數,讀取檔案初始位置;檔案大小以位元組為機關
 * callback(err, bytesRead, buffer), 讀取執行完成後回調函數,bytesRead實際讀取位元組數,被讀取的緩存區對象
 */

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);
    //每一個漢字utf8編碼是3個位元組,英文是1個位元組
    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讀取時,基點是基于上次讀取位置計算;
        fs.read(fd, buffer, 0, 9, null, function (err, bytesRead, buffer) {
          console.log(bytesRead);
          console.log(buffer.slice(0, bytesRead).toString());
        });
      }
    });
  }
});
           

6、寫檔案,将緩沖區内資料寫入使用fs.open打開的檔案

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

/**
 * fd, 使用fs.open打開成功後傳回的檔案描述符
 * buffer, 一個Buffer對象,v8引擎配置設定的一段記憶體
 * offset, 整數,從緩存區中讀取時的初始位置,以位元組為機關
 * length, 整數,從緩存區中讀取資料的位元組數
 * position, 整數,寫入檔案初始位置;
 * callback(err, written, buffer), 寫入操作執行完成後回調函數,written實際寫入位元組數,buffer被讀取的緩存區對象
 */

fs.open(__dirname + '/test.txt', 'a', function (err, fd) {
  if(err) {
    console.error(err);
    return;
  } else {
    var buffer = new Buffer('寫入檔案資料内容');
    //寫入'入檔案'三個字
    fs.write(fd, buffer, 3, 9, 12, function (err, written, buffer) {
      if(err) {
        console.log('寫入檔案失敗');
        console.error(err);
        return;
      } else {
        console.log(buffer.toString());
        //寫入'資料内'三個字
        fs.write(fd, buffer, 12, 9, null, function (err, written, buffer) {
          console.log(buffer.toString());
        })
      }
    });
  }
});
           

7、重新整理緩存區;

// 使用fs.write寫入檔案時,作業系統是将資料讀到記憶體,再把資料寫入到檔案中,當資料讀完時并不代表資料已經寫完,因為有一部分還可能在内在緩沖區内。
// 是以可以使用fs.fsync方法将記憶體中資料寫入檔案;--重新整理記憶體緩沖區;

//fs.fsync(fd, [callback])
/**
 * fd, 使用fs.open打開成功後傳回的檔案描述符
 * [callback(err, written, buffer)], 寫入操作執行完成後回調函數,written實際寫入位元組數,buffer被讀取的緩存區對象
 */

fs.open(__dirname + '/test.txt', 'a', function (err, fd) {
  if(err)
    throw err;
  var buffer = new Buffer('我愛nodejs程式設計');
  fs.write(fd, buffer, 0, 9, 0, function (err, written, buffer) {
    console.log(written.toString());
    fs.write(fd, buffer, 9, buffer.length - 9, null, function (err, written) {
      console.log(written.toString());
      fs.fsync(fd);
      fs.close(fd);
    })
  });
});
           

8、建立目錄;

//使用fs.mkdir建立目錄
//fs.mkdir(path, [mode], callback);

/**
 * path, 被建立目錄的完整路徑及目錄名;
 * [mode], 目錄權限,預設0777
 * [callback(err)], 建立完目錄回調函數,err錯誤對象
 */

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

9、讀取目錄;

//使用fs.readdir讀取目錄,重點其回調函數中files對象
//fs.readdir(path, callback);

/**
 * path, 要讀取目錄的完整路徑及目錄名;
 * [callback(err, files)], 讀完目錄回調函數;err錯誤對象,files數組,存放讀取到的目錄中的所有檔案名
 */

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');
        }
      });
    });
    for (var i = 0; i < files.length; i++) {
      //使用閉包無法保證讀取檔案的順序與數組中儲存的緻
      (function () {
        var filePath = path.normalize(__dirname + '/fsDir/' + files[i]);
        fs.stat(filePath, function (err, stat) {
          if(stat.isFile()) {
            console.log(filePath + ' is: ' + 'file');
          }
          if(stat.isDirectory()) {
            console.log(filePath + ' is: ' + 'dir');
          }
        });
      })();
    }
  }
});
           

10、檢視檔案與目錄的資訊;

//fs.stat(path, callback);
//fs.lstat(path, callback); //檢視符号連結檔案
/**
 * path, 要檢視目錄/檔案的完整路徑及名;
 * [callback(err, stats)], 操作完成回調函數;err錯誤對象,stat fs.Stat一個對象執行個體,提供如:isFile, isDirectory,isBlockDevice等方法及size,ctime,mtime等屬性
 */

//執行個體,檢視fs.readdir
           

11、檢視檔案與目錄的是否存在

//fs.exists(path, callback);

/**
 * path, 要檢視目錄/檔案的完整路徑及名;
 * [callback(exists)], 操作完成回調函數;exists true存在,false表示不存在
 */

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

12、修改檔案通路時間與修改時間

//fs.utimes(path, atime, mtime, callback);

/**
 * path, 要檢視目錄/檔案的完整路徑及名;
 * atime, 新的通路時間
 * ctime, 新的修改時間
 * [callback(err)], 操作完成回調函數;err操作失敗對象
 */

fs.utimes(__dirname + '/test.txt', new Date(), new Date(), function (err) {
  if(err) {
    console.error(err);
    return;
  }
  fs.stat(__dirname + '/test.txt', function (err, stat) {
    console.log('通路時間: ' + stat.atime.toString() + '; \n修改時間:' + stat.mtime);
    console.log(stat.mode);
  })
});
           

13、修改檔案或目錄的操作權限

//fs.utimes(path, mode, callback);

/**
 * path, 要檢視目錄/檔案的完整路徑及名;
 * mode, 指定權限,如:0666 8進制,權限:所有使用者可讀、寫,
 * [callback(err)], 操作完成回調函數;err操作失敗對象
 */

fs.chmod(__dirname + '/fsDir', 0666, function (err) {
  if(err) {
    console.error(err);
    return;
  }
  console.log('修改權限成功')
});
           

14、移動/重命名檔案或目錄

//fs.rename(oldPath, newPath, callback);

/**
 * oldPath, 原目錄/檔案的完整路徑及名;
 * newPath, 新目錄/檔案的完整路徑及名;如果新路徑與原路徑相同,而隻檔案名不同,則是重命名
 * [callback(err)], 操作完成回調函數;err操作失敗對象
 */
fs.rename(__dirname + '/test', __dirname + '/fsDir', function (err) {
  if(err) {
    console.error(err);
    return;
  }
  console.log('重命名成功')
});
           

15、删除空目錄

//fs.rmdir(path, callback);

/**
 * path, 目錄的完整路徑及目錄名;
 * [callback(err)], 操作完成回調函數;err操作失敗對象
 */

fs.rmdir(__dirname + '/test', function (err) {
  fs.mkdir(__dirname + '/test', 0666, function (err) {
    console.log('建立test目錄');
  });
  if(err) {
    console.log('删除空目錄失敗,可能原因:1、目錄不存在,2、目錄不為空')
    console.error(err);
    return;
  }
  console.log('删除空目錄成功!');
});
           

16、監視檔案

//對檔案進行監視,并且在監視到檔案被修改時執行處理
//fs.watchFile(filename, [options], listener);

/**
 * filename, 完整路徑及檔案名;
 * [options], persistent true表示持續監視,不退出程式;interval 機關毫秒,表示每隔多少毫秒監視一次檔案
 * listener, 檔案發生變化時回調,有兩個參數:curr為一個fs.Stat對象,被修改後檔案,prev,一個fs.Stat對象,表示修改前對象
 */
 
fs.watchFile(__dirname + '/test.txt', {interval: 20}, function (curr, prev) {
  if(Date.parse(prev.ctime) == 0) {
    console.log('檔案被建立!');
  } 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('這是第二個watch,監視到檔案有修改');
});
           

17、取消監視檔案

//取消對檔案進行監視
//fs.unwatchFile(filename, [listener]);

/**
 * filename, 完整路徑及檔案名;
 * [listener], 要取消的監聽器事件,如果不指定,則取消所有監聽處理事件
 */

var listener = function (curr, prev) {
  console.log('我是監視函數')
}
fs.unwatchFile(__dirname + '/test.txt', listener);
           

18、監視檔案或目錄

// 對檔案或目錄進行監視,并且在監視到修改時執行處理;
// fs.watch傳回一個fs.FSWatcher對象,擁有一個close方法,用于停止watch操作;
// 當fs.watch有檔案變化時,會觸發fs.FSWatcher對象的change(err, filename)事件,err錯誤對象,filename發生變化的檔案名
// fs.watch(filename, [options], [listener]);

/**
 * filename, 完整路徑及檔案名或目錄名;
 * [listener(event, filename], 監聽器事件,有兩個參數:event 為rename表示指定的檔案或目錄中有重命名、删除或移動操作或change表示有修改,filename表示發生變化的檔案路徑
 */

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 + ' 發生變化')
});

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

19、檔案流

/*
 * 流,在應用程式中表示一組有序的、有起點有終點的位元組資料的傳輸手段;
 * Node.js中實作了stream.Readable/stream.Writeable接口的對象進行流資料讀寫;以上接口都繼承自EventEmitter類,是以在讀/寫流不同狀态時,觸發不同僚件;
 * 關于流讀取:Node.js不斷将檔案一小塊内容讀入緩沖區,再從緩沖區中讀取内容;
 * 關于流寫入:Node.js不斷将流資料寫入内在緩沖區,待緩沖區滿後再将緩沖區寫入到檔案中;重複上面操作直到要寫入内容寫寫完;
 * readFile、read、writeFile、write都是将整個檔案放入記憶體而再操作,而則是檔案一部分資料一部分資料操作;
 *
 * -----------------------流讀取-------------------------------------
 * 讀取資料對象:
 * fs.ReadStream 讀取檔案
 * http.IncomingMessage 用戶端請求或伺服器端響應
 * net.Socket    Socket端口對象
 * child.stdout  子程序标準輸出
 * child.stdin   子程序标準入
 * process.stdin 用于建立程序标準輸入流
 * Gzip、Deflate、DeflateRaw   資料壓縮
 *
 * 觸發事件:
 * readable  資料可讀時
 * data      資料讀取後
 * end       資料讀取完成時
 * error     資料讀取錯誤時
 * close     關閉流對象時
 *
 * 讀取資料的對象操作方法:
 * read      讀取資料方法
 * setEncoding   設定讀取資料的編
 * pause     通知對象衆目停止觸發data事件
 * resume    通知對象恢複觸發data事件
 * pipe      設定資料通道,将讀入流資料接入寫入流;
 * unpipe    取消通道
 * unshift   當流資料綁定一個解析器時,此方法取消解析器
 *
 * ------------------------流寫入-------------------------------------
 * 寫資料對象:
 * fs.WriteStream           寫入檔案對象
 * http.clientRequest       寫入HTTP用戶端請求資料
 * http.ServerResponse      寫入HTTP伺服器端響應資料
 * net.Socket               讀寫TCP流或UNIX流,需要connection事件傳遞給使用者
 * child.stdout             子程序标準輸出
 * child.stdin              子程序标準入
 * Gzip、Deflate、DeflateRaw  資料壓縮
 *
 * 寫入資料觸發事件:
 * drain            當write方法傳回false時,表示緩存區中已經輸出到目标對象中,可以繼續寫入資料到緩存區
 * finish           當end方法調用,全部資料寫入完成
 * pipe             當用于讀取資料的對象的pipe方法被調用時
 * unpipe           當unpipe方法被調用
 * error            當發生錯誤
 *
 * 寫入資料方法:
 * write            用于寫入資料
 * end              結束寫入,之後再寫入會報錯;
 */
           

20、建立讀取流

//fs.createReadStream(path, [options])
/**
 * path 檔案路徑
 * [options] flags:指定檔案操作,預設'r',讀操作;encoding,指定讀取流編碼;autoClose, 是否讀取完成後自動關閉,預設true;start指定檔案開始讀取位置;end指定檔案開始讀結束位置
 */

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

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

rs.on('end', function () {
  console.log('讀取檔案結束')
});
rs.on('close', function () {
  console.log('檔案關閉');
});

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

//暫停和回複檔案讀取;
rs.on('open', function () {
  console.log('開始讀取檔案');
});

rs.pause();

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

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

21、建立寫入流

//fs.createWriteStream(path, [options])
/**
 * path 檔案路徑
 * [options] flags:指定檔案操作,預設'w',;encoding,指定讀取流編碼;start指定寫入檔案的位置
 */

/* ws.write(chunk, [encoding], [callback]);
 * chunk,  可以為Buffer對象或一個字元串,要寫入的資料
 * [encoding],  編碼
 * [callback],  寫入後回調
 */

/* ws.end([chunk], [encoding], [callback]);
 * [chunk],  要寫入的資料
 * [encoding],  編碼
 * [callback],  寫入後回調
 */

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('寫入完成,回調函數沒有參數')
});
//最後再寫入的内容
ws.end('再見');
//使用流完成複制檔案操作
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('要寫入的資料檔案已經打開,檔案描述符是: ' + fd);
});

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

 
//關于WriteStream對象的write方法傳回一個布爾類型,當緩存區中資料全部寫滿時,傳回false;
//表示緩存區寫滿,并将立即輸出到目标對象中
 
//第一個例子
var ws = fs.createWriteStream(__dirname + '/test/test.txt');
for (var i = 0; i < 10000; i++) {
  var w_flag = ws.write(i.toString());
  //當緩存區寫滿時,輸出false
  console.log(w_flag);
}


//第二個例子
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);
});

//系統緩存區資料已經全部輸出觸發drain事件
ws.on('drain', function () {
  console.log('系統緩存區資料已經全部輸出。')
});
           

22、管道pipe實作流讀寫

//rs.pipe(destination, [options]);
/**
 * destination 必須一個可寫入流資料對象
 * [opations] end 預設為true,表示讀取完成立即關閉檔案;
 */

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('資料可讀')
});
rs.on('end', function () {
  console.log('檔案讀取完成');
  //ws.end('再見')
});
           

<4>node.js高階子產品-fs子產品

 NodeJs隻能做的兩件事是什麼?

實際上,所有背景語言都能做的兩件事是: 檔案操作和網絡程式設計.

這其實是所有語言的根本。 計算機無外乎就是檔案和通信。Linux中,是把一切都當做檔案,如果了解了這一點那就無可厚非了.

是以,這裡,我想介紹一下NodeJS中一個重要的子產品--fs.

這裡我給大家放一個我的架構圖~

NodeJs 的fs子產品對檔案的操作Node.js之fs用法詳解

(為什麼不是http? 懶~)

let's start.

針對于fs,我們切實圍繞幾個問題來吧~

  1. fs是如何操作檔案的?
  2. drain和write到底是什麼關系?
  3. fs怎麼寫出向gulp那樣實時監聽檔案改變的插件?

關于fs的API,直接參考Nodejs官網. 同樣,放上fs的基本架構圖:

NodeJs 的fs子產品對檔案的操作Node.js之fs用法詳解

(圖有點大,大家另外開一個視窗看吧)

我們圍繞這些問題來展開,說明吧.

fs操作檔案的幾種方式

這裡,我們針對檔案最基本的兩種操作進行相關的解釋和說明吧--read&&write

讀寫檔案有哪幾種操作方式呢?

我們先從最簡便的開始吧~

先熟悉API: fs.createReadStream(path[, options]) path就是打開的檔案路徑,options有點複雜:

{
  flags: 'r',
  encoding: null,
  fd: null,
  mode: 0o666,
  autoClose: true
}
           

實際上我們一般也用不到options,除非你是擷取已經打開後的檔案.具體描述詳見.官網.

ok~ 現在正式打開一個檔案:

const fs = require('fs');
const read = fs.createReadStream('sam.js',{encoding:'utf8'});
read.on('data',(str)=>{
    console.log(str);
})
read.on('end',()=>{
    console.log('have already opened');
})
           

實際上,我們就是利用fs繼承的readStream來進行操作的.

使用open打開檔案

同樣上:API:

fs.open(path, flags[, mode], callback)這個和上面的readStream不同,open打開檔案是一個持續狀态,相當于會将檔案寫入到記憶體當中. 而readStream隻是讀取檔案,當讀取完畢時則會自動關閉檔案--相當于fs.open+fs.close兩者的結合~ 其中flags和mode 就是設定打開檔案的權限,以及檔案的權限模式(rwx). 

使用open來打開一個檔案

const fs = require('fs');
fs.open('sam.js','r',(err,fd)=>{
    fs.fstat(fd,(err,stat)=>{
        var len = stat.size;  //檢測檔案長度
        var buf = new Buffer(len);
        fs.read(fd,buf,0,len,0,(err,bw,buf)=>{
            console.log(buf.toString('utf8'));
            fs.close(fd);
        })
    });
});
           

使用相關的read/readdir/readFile/readlink

read方法,使用來讀取已經打開後的檔案。 他不用用來進行打開檔案操作,這點很重要》 那還有其他方法,在讀的過程可以直接打開檔案嗎? 

absolutely~

這裡就拿readFile和readdir舉例吧

API

fs.readFile(file[, options], callback): file就是檔案路徑,options可以為object也可以為string. 不過最常用的還是str. 我們直接看demo:

const fs = require('fs');
fs.readFile('sam.js','utf8',(err,data)=>{
    console.log(`the content is ,${data}`);
})
           

另外一個readdir,顧名思義該API就是用來讀取檔案夾的.實際上,該API也沒有什麼卵用~

fs.readdir(path, callback):用來擷取檔案下所有的檔案(包括目錄),并且不會進行recursive.并且callback(err,files)中的files隻是以數組的形式放回該目錄下所有檔案的名字

show u code:

//用來檢查,上層目錄中那些是file,那些是dir
const fs = require('fs');
fs.readdir('..', (err,files)=>{
    var path,stat;
    files.map((val)=>{
        path = `../${val}`;
        stat= fs.statSync(path);
        if(stat.isFile()){
            console.log(`file includes ${val}`);
        }else if(stat.isDirectory()){
            console.log(`dir includes ${val}`);
        }
    })
})
           

nodejs 打開檔案的所有方式就是以上這幾種.接下來我們再來看一下,如果寫入檔案吧~

寫入檔案

同樣,先介紹最簡單的吧.

fs.createWriteStream(path[, options]): path就是檔案路徑.而options和上面的createWriteStream一樣比較複雜;

{
  flags: 'w',
  defaultEncoding: 'utf8',
  fd: null,
  mode: 0o666
}
           

實際上,我們隻需要寫好path就enough了.

直接看demo吧:

//用來寫入str的操作
const fs = require('fs');
const write = fs.createWriteStream('sam.js');
write.on('drain',()=>{
    write.resume();
});
var writeData = function(){
    var i = 1000;
    while(i--){
        if(!write.write('sam')){
            write.pause();
        }
    }
}
writeData();
           

實際上,上面那段代碼是最常用的寫入檔案的寫法.drain是代表,寫入記憶體已經清空後,可以繼續寫入時觸發的事件.這就是第二個問題: drain和write到底是什麼關系? 這個問題,我們放到後面講解,這裡先繼續說一下如何寫入内容.

使用fs.write方法直接寫入内容:

fs.writeAPI其實就有兩個:

fs.write(fd, buffer, offset, length[, position], callback):這一種,是用來直接寫入Buffer資料内容的. 

fs.write(fd, data[, position[, encoding]], callback):這一種,是用來寫入str資料内容的.

不過,fs.write()該方法,也是建立在已有檔案打開的基礎上的.

直接看一下demo:

//使用Buffer寫入
const fs = require('fs');
fs.open('sam.js','w+',(err,fd)=>{
    var buf = new Buffer("sam",'utf8');
    fs.write(fd,buf,0,buf.length,0,(err,bw,buf)=>{
        fs.close(fd);
    });
})
//直接使用string寫入:
const fs = require('fs');
fs.open('sam.js','w+',(err,fd)=>{
    fs.write(fd,'sam','utf8',0,(err,bw,buf)=>{
        fs.close(fd);
    });
})
           

通常情況下,我們也不會用來寫入Buffer的. 是以,第二種方法就足夠了.

同理,能否直接寫入未打開的檔案呢?

當然是可以的,是以這裡介紹最後一種方法. 使用writeFile和appendFile來寫入資料.

fs.writeFile(file, data[, options], callback):直接寫入指定檔案. 寫入的内容會直接覆寫掉原始内容.

fs.appendFile(file, data[, options], callback):真正的用來append file

//檢測檔案是否存在,如果存在則增加内容,否則建立檔案并寫入内容.
const fs = require('fs');
var writeData = function() {
    fs.access('sam.js', (noAccess) => {
        if (noAccess) {
            fs.writeFile('sam.js', 'sam', (err) => {
                if (!err) console.log('writeFile success')
            })
        } else {
            fs.appendFile('sam.js', 'sam', (err) => {
                if (!err) console.log('appendFile success~');
            });
        }
    })
}
writeData()
           

大緻梳理一下上面說的内容吧:

NodeJs 的fs子產品對檔案的操作Node.js之fs用法詳解

drain和stream.write的關聯

首先這兩個東西,是底層writeStream提供的. write這個方法不用解釋了吧~ 關鍵drain到底怎麼使用~ 這也是官網沒說清楚的地方:

If a stream.write(chunk) call returns false, then the 'drain' event will indicate when it is appropriate to begin writing more data to the stream.

實際上,我們判斷用沒用到drain事件的機制,是根據write方法的傳回值來進行判斷的. 官方也給出一個demo,用來測試drain事件的觸發.

const fs = require('fs');
const writer = fs.createWriteStream('sam.js');
writeOneMillionTimes(writer,'sam','utf8',()=>{});
           

沒錯,這樣确實會多次觸發drain事件.但是,他到底是什麼時候會觸發呢?

根據源碼的介紹,write方法在使用時,會内置一個Buffer用來寫入資料.我們可以了解該Buffer就是該次寫入的最大記憶體值~ 那到底是多少呢? 源碼:

var defaultHwm = this.objectMode ? 16 : 16 * 1024;
//即,預設為16KB
           

相當于,我們每次寫入都會有16KB的空間開着~ 如果寫入data已經填滿了16KB, 而我們還繼續寫入就有可能造成 memory leak~ 這就go die了。輕者卡一卡,重則當機都有可能. 那如果按照官網那種寫法的話,每次寫入一個大檔案,都要寫入老長老長的函數呢?

倫家~才不要~

實際上,我們直接提煉一下,使用stream.once('drain')事件來進行處理.

if(!stream.write(data))stream.once('drain',()=>{
    stream.write(otherData);
})
           

或者當你使用readStream和writeStream用來讀寫檔案時~可以使用

//試一試讀取一個較大的檔案,你會發現drain事件也會觸發~ 是以我們需要使用pause和resume來暫停流的讀取,防止memory leak~
const fs = require('fs');
const read = fs.createReadStream('唱歌的孩子.mp3');
const write = fs.createWriteStream('get.mp3');
read.on('data',(chunk)=>{
    if(!write.write(chunk))read.pause();
});
write.on('drain',()=>{
    console.log('drain');
    read.resume();
})
read.on('end',()=>{
    console.log(`finish`);
})
           

如何寫出像gulp一樣監聽檔案變化的插件呢?

首先,我們看一下監聽插件的配置:

gulp.task('sync', function() {
    var files = [
        'app/**/*.html',
        'app/styles/**/*.css',
        'app/img/**/*.png',
        'app/src/**/*.js'
    ];
    browserSync.init(files, {
        server: {
            baseDir: './app'
        }
    });
});
           

首先,我們設定了files之後,就可以監聽檔案,并且開啟一個服務~

而實際上,就是使用Nodejs底層的fs.watch對檔案進行監聽.我們來使用fs.watch和fs.watchFile來實作檔案的監聽~

這裡,我們先從簡單的watchFile入手~ 

根據nitoyon的解釋,我們可以得出兩個結論

  • fs.watch() uses native API
  • fs.watchFile() periodically executes fs.stat()

是以,底層上來看,其實fs.watchFile是周期性執行fs.stat的,速度上來看,肯定會慢的. 不多說了,我們看一下demo:

const fs = require('fs');
fs.watchFile('sam.js', {
    persistent:true,
    interval:3000
}, (cur,prev)=>{
    if(cur.mtime>prev.mtime){
        console.log('change');
        console.log(cur,prev);
    }
})
           

這裡,主要想談及一下watchFile中的第二個參數,options中的interval. 這個東西有點傻逼~ 為什麼呢? 因為,他并不是在一定時間内,觸發watch,而是在第一次觸發後的interval時間内,不會觸發watch. 即,他會發生改變的積累~ 在interval時間内改變的内容,隻會在最後一次中呈現出來~ 而他的底層其實就是調用fs.stat來完成的.這裡,我們使用fs.stat來模仿一遍~

const fs = require('fs'),
    Event = require('events').EventEmitter,
    event = new Event();


//原始方法getCur
//原始屬性prev
var watchFile = function(file,interval,cb){
    var pre,cur;
    var getPrv = function(file){
        var stat = fs.statSync(file);
        return stat;
    }
    var getCur = function(file){
        cur = getPrv(file);
        console.log(cur,pre);
        if(cur.mtime.toString()!==pre.mtime.toString()){
            cb('change');
        }
        pre = cur; //改變初始狀态
    }
    var init = (function(){
        pre = getPrv(file); //首先擷取pre
        event.on('change',function(){
            getCur(file);
        });
        setInterval(()=>{
            event.emit('change');
        },interval);
    })()
}
watchFile('sam.js',2000,function(eventname){
    console.log(eventname);
})
           

上述,完善了一下,在指定時間内,對檔案改動進行監聽,和fs.watchFile不同.

ok~ 這個out-of-date的監聽方式,我們大緻了解了. 接下來我們來看一下,如何使用v0.5.x版本退出的新API:fs.watch. 我們參考官網:

fs.watch should be used instead of fs.watchFile and fs.unwatchFile when possible.

為什麼呢?

不為什麼. 因為,fs.watch調用的是native API。而fs.watchFile是調用的是fs.stat. 比起來,時間肯定會慢一點.

那怎麼使用fs.watch監聽檔案呢?

先看一下API吧:

fs.watch(filename, options):其實和fs.watchFile沒差多少. 不多options裡面有一個參數不同:

{ persistent: true, recursive: false }
           

即,該API不僅可以監聽檔案,還可以監聽目錄.其中recursive表示遞歸,用來監聽目錄下的檔案。 不過NodeJS如是說:

The recursive option is only supported on OS X and Windows.

懂了吧. 不過基本上, 該API的覆寫率也足夠了.别告訴我,你用linxu寫代碼.

const fs = require('fs');
fs.watch('..',{recursive:true},function(event,filename){
    console.log(`event is ${event} and filename is ${filename}`);
})
           

在MAC OX 11完美通過. 每當儲存一次,就會觸發一次。不過當你修改檔案名時,便會觸發兩次. 一次是,原檔案被修改,另一次是新檔案被建立.即.

event is rename and filename is app/sam.html
event is rename and filename is app/index.html
           

<5>node.js裡面的fs用法詳解

Node.js之fs用法詳解

2018-01-22

Node.js 内置的fs子產品就是檔案系統子產品,負責讀寫檔案。和所有其他JS子產品不同的是,fs子產品同時提供了異步和同步的方法。

  • 檔案寫入
1
2
3
4
5
6
7
8
9
      
var fs = require("fs");
//       要寫入的檔案   要寫入的内容       a追加|w寫入(預設)|r(讀取)  回調函數
fs.writeFile("11.txt","我是要寫入的11.txt檔案的内容",{flag:"a"},function (err) {
    if(err){
        return console.log(err);
    }else {
        console.log("寫入成功");
    }
})
      

運作上述代碼的時候,會發現該父級檔案夾下會自動生成一個11.txt檔案。

1
2
3
4
5
6
7
8
      
fs.appendFile("11.txt","這是要追加的内容",function (err) {
    if(err){
        return console.log(err);
    }else {
        console.log("追加成功");
    }

})
      
NodeJs 的fs子產品對檔案的操作Node.js之fs用法詳解

因為是追加的内容,是以内容會自動在該檔案後面

上面說的方法都是異步操作,異步操作會傳回一個回調函數,在回調函數裡面執行結束語句,不然會出現錯誤

而所有的同步函數,都隻是在異步函數後面加上Sync

1
2
      
var res = fs.writeFileSync("11.txt","這裡面是使用同步方法寫的内容");
console.log(res);
      
  • 檔案讀取

異步方法讀取檔案

1
2
3
4
5
6
7
8
9
      
//檔案讀取
fs.readFile("11.txt",function (err,data) {
    if(err){
        return console.log(err);
    }else {
        //toString() 将buffer格式轉化為中文
        console.log(data.toString());
    }
})
      

如果使用同步的方法,不需要在後面使用回調方法

1
2
      
var data = fs.readFileSync("11.txt");
console.log(data.toString());
      
  • 檔案修改
1
2
3
4
5
6
7
8
      
//    要修改名字的檔案  修改後的名字  回調函數
fs.rename("11.txt","22.txt",function (err) {
    if(err){
        console.log(err);
    }else {
        console.log("修改成功");
    }
})
      
  • 檔案删除
1
2
3
4
5
6
7
8
      
//删除檔案
fs.unlink("11.txt",function (err) {
    if(err){
        return console.log(err);
    }else {
        console.log("删除成功");
    }
})
      
  • 檔案複制(先讀取,在複制)

異步方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
      
fs.readFile("22.txt",function (err,data) {
    if(err){
        return console.log(err);
    }else{
        var getData = data.toString();
        fs.writeFile("33.txt",getData,function (err) {
            if(err){
                return console.log(err);
            }else {
                console.log("複制歐克");
            }
        })
    }
})
      

同步方法,相比異步少了很對回調

1
2
      
var res = fs.writeFileSync("44.txt",fs.readFileSync("22.txt"));
console.log(res);
      
  • 檔案夾建立
1
2
3
4
5
6
7
8
9
      
//檔案夾建立
//1 -- 執行   2 -- 寫入  4 -- 讀取  7=1+2+4  以為建立的檔案夾可執行可讀可寫
fs.mkdir("img",0777,function (err) {
    if(err){
        console.log(err);
    }else {
        console.log("建立成功");
    }
})
      

上述的權限就是在檔案簡介裡面權限

NodeJs 的fs子產品對檔案的操作Node.js之fs用法詳解
  • 修改檔案夾權限
1
2
3
4
5
6
7
      
fs.chmod("img",0333,function (err) {
    if(err){
        return console.log(err);
    }else {
        console.log("修改ok");
    }
})
      
NodeJs 的fs子產品對檔案的操作Node.js之fs用法詳解
  • 修改檔案夾名字,與修改檔案是同一個函數
1
2
3
4
5
6
7
8
      
//修改檔案夾名稱
fs.rename("img","image",function (err) {
    if(err){
        return console.log(err);
    }else {
        console.log("修好");
    }
})
      
  • 判斷某個檔案件是否存在,如果不存在建立,exists函數,是唯一一個回調函數中不帶err的回調函數
1
2
3
4
5
6
7
8
9
10
11
12
13
      
fs.exists("img",function (exists) {
    if(exists){
        console.log("該檔案夾已經存在");
    }else {
        fs.mkdir("img",function (err) {
            if(err){
                return console.log(err);
            }else {
                console.log("建立成功");
            }
        })
    }
})
      
  • 删除檔案夾(隻能删除空的檔案夾)
1
2
3
4
5
6
7
      
fs.rmdir("img",function (err) {
    if(err){
        return console.log(err);
    }else {
        console.log("删除成功");
    }
})
      
  • 讀取檔案夾裡面的資訊
1
2
3
4
5
6
7
      
fs.readdir("image",function (err,data) {
    if(err){
        console.log(err);
    }else {
        console.log(data);
    }
})
      
NodeJs 的fs子產品對檔案的操作Node.js之fs用法詳解
  • 判斷一個位置問價是否是檔案或者是檔案件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
          
    fs.stat("image",function (err,data) {
        if(err){
            return console.log(err);
        }else {
            //判斷是否是檔案
            if(data.isFile()){
                //是檔案
                console.log("yes");
            }else{
                //是檔案夾
                console.log("no");
            }
        }
    })
          
  • 删除非空檔案夾

    首先擷取到該檔案夾裡面所有的資訊,周遊裡面的資訊,判斷是檔案還是檔案夾,如果是檔案直接删除,如果是檔案,進入檔案,重複上述過程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
      
function delFile(url) {
    var data = fs.readdirSync(url);
    for(var i = 0;i < data.length;i++){
        // console.log(data[i])
        var path = url + "/" +data[i];
        console.log(path);
        var stat = fs.statSync(path);
        if(stat.isFile()){
            fs.unlinkSync(path);
        }else{
            delFile(path);
        }
    }
    fs.rmdirSync(url);
}
delFile("image");