Node中的fs模块用来对本地文件系统进行操作。文件的I/O是由标准POSIX函数封装而成。需要使用require('fs')访问这个模块。
fs模块中提供的方法可以用来执行基本的文件操作,包括读、写、重命名、创建和删除目录以及获取文件元数据等。每个操作文件的方法都有同步和异步两个版本。
异步操作的版本都会使用一个回调方法作为最后一个参数。当操作完成的时候,该回调方法会被调用。而回调方法的第一个参数总是保留为操作时可能出现的异常。如果操作正确成功,则第一个参数的值是null或undefined。
同步操作的版本的方法名称则是在对应的异步方法之后加上一个Sync作为后缀。比如异步的rename() 方法的同步版本是renameSync()。
fs.readFile属于fs模块,使用前需要首先引入fs模块
const fs= require(“fs”)fs.readFile以异步的方式读取文件内容。
注意,文件编码可取值包括’ascii’/‘utf-8’/‘base64’,如果没有指定文件编码,返回的是原始的缓存二进制数据,这时需要调用buffer对象的toString方法将其转换为字符串。
const fs = require('fs'); console.log('start'); fs.readFile('./test.txt', 'utf-8', (error, file) => { console.log(file); }); // fs.readFile('./test.txt', (error, file) => { // console.log('before:', file.toString()); // }); console.log('finish');输出结果:
start finish Content: 你好啊输出结果:
start finish write finish Content: 难判谁人错与对注意,在同一个文件多次使用fs.writeFile且不等待回调是不安全的,对于这种情况应该使用fs.createWriteStrem
异步地将数据追加到文件,如果文件尚不存在则创建该文件。 data可以是字符串或Buffer。
const fs = require('fs'); fs.readFile('./test.txt', 'utf-8', (error, file) => { console.log('before:', file); }); fs.appendFile('./test.txt', '你随河流而来', 'utf-8', (error) => { fs.readFile('./test.txt', 'utf-8', (error, file) => { console.log('after:', file); }); });结果:
before: 难判谁人错与对 after: 难判谁人错与对 你随河流而来对文件进行监视,并且在监视到文件被修改时执行处理
filename, 完整路径及文件名;[options], persistent为true表示持续监视,不退出程序;interval单位毫秒,表示每隔多少毫秒监视一次文件listener, 文件发生变化时回调,有两个参数:curr为修改后的fs.Stat对象,prev为修改之前的fs.Sta对象 fs.watchFile('./test.txt', (curr, prev) => { console.log('current', curr); console.log('previous', prev); });改动文件后,输出结果:
current Stats { dev: 16777218, mode: 33188, nlink: 1, uid: 501, gid: 20, rdev: 0, blksize: 4096, ino: 4636749, size: 46, blocks: 8, atimeMs: 1556524554000, mtimeMs: 1556524553000, ctimeMs: 1556524553000, birthtimeMs: 1556524553000, atime: 2019-04-29T07:55:54.000Z, mtime: 2019-04-29T07:55:53.000Z, ctime: 2019-04-29T07:55:53.000Z, birthtime: 2019-04-29T07:55:53.000Z } previous Stats { dev: 16777218, mode: 33188, nlink: 1, uid: 501, gid: 20, rdev: 0, blksize: 4096, ino: 4636495, size: 42, blocks: 8, atimeMs: 1556524547000, mtimeMs: 1556524320000, ctimeMs: 1556524320000, birthtimeMs: 1556524319000, atime: 2019-04-29T07:55:47.000Z, mtime: 2019-04-29T07:52:00.000Z, ctime: 2019-04-29T07:52:00.000Z, birthtime: 2019-04-29T07:51:59.000Z }fs.unwatfile方法用来接触对文件的监听。
原本判断路径是否存在的fs.exists已经在V1.0.0被废弃,改为使用fs.stat或者fs.access
fa.stat用来查询文件信息
fs.stat('./test.txt', (err, stat) => { if (stat && stat.isFile()) { console.log(stat); } else { console.log('file does not exist'); } });fs.access用来检查文件的访问权限
// 检查文件是否存在 fs.access('/etc/passwd', function(err) { console.log(err ? '文件存在' : '文件不存在'); }); // 检查文件是否有读写权限 fs.access('/etc/passwd', fs.R_OK | fs.W_OK, function(err) { console.lo(err ? '不可操作!' : '可以读/写'); });mkdir接受三个参数吗,第一个是目录名,第二个是权限制,第三个是回调函数
fs.mkdir('./hello', '0777', err => console.log(err));用来读取目录,返回一个所包含的文件和子目录的数组,不会递归寻找内部目录,需要手动递归实现:
const list = (path) => { fs.readdir(path, (err, files) => { console.log(files); files.forEach(file => { fs.stat(file, (error, stat) => { if(stat && stat.isDirectory()) { list(file) } }) }) }) }; list(process.cwd());createReadStrem方法旺旺用来打开大型的文本文件,创建一个读取操作的数据流。大型文本文件体积很大,读取操作的缓存装不下,只能分成几次发送。每次发送会触发data时间,发送结束会触发end时间
const func = data => { console.log('Line: ' + data); }; const readLines = (input, func) => { let remaining = ''; input.on('data', data => { remaining += data; let index = remaining.indexOf('\n'); let last = 0; while(index > -1) { let line = remaining.substring(last, index); last = index + 1; func(line); index = remaining.indexOf('\n', last); } remaining = remaining.substring(last); }); input.on('end', () => { if(remaining.length > 0) { func(remaining) } }) }; const input = fs.createReadStream('./test.txt'); readLines(input, func);createWriteStream用来创建一个写入数据流对象,该对象的write方法用于写入数据,end方法用于结束写入操作
const out = fs.createWriteStream(fileName, { encoding: 'utf8' }); out.write(str); out.end();createReadStream和createWriteStream配合,可以实现拷贝大型文件。
const fileCopy = (filename1, filename2, done) => { const input = fs.createReadStream(filename1); const output = fs.createWriteStream(filename2); input.on('data', data => { output.write(data); }); input.on('error', err => { console.log(err); }); input.on('end', () => { output.end(); if (done) { done() } }) }; fileCopy('./test.txt', './hello.text', () => { console.log('copy finish'); });如果world文件夹不为空,无法删除:
{ [Error: ENOTEMPTY: directory not empty, rmdir './world'] errno: -66, code: 'ENOTEMPTY', syscall: 'rmdir', path: './world' }上面提到的异步API都有着对应的同步方法,比如readFile对应的同步方法readFileSync():
const fs = require("fs"); const file = fs.readFileSync("./test.txt", "utf-8"); console.log(file); console.log("finish");执行的顺序就是按照一条时间链由上至下执行:
你好哇~ finishfs-extra模块是fs模块的一个扩展,文档在这里。
fs-extra集成了fs模块的所有方法,提供了很多遍历的API,并且添加了Promise的支持,可以使用它来代替fs模块。
它不是Node自带的模块,需要单独安装
npm i -S fs-extra使用时完全可以替代fs模块,如果希望明确表示在使用fs-exra,可以将fs标示改为fse
const fse = require('fs-extra');fs-extra的异步方法可以使用回调函数的形式,更推荐的是使用Promise形式,也可以使用Async/Await的语法:
// 回调函数 fse.copy('./test.txt', './world/test.txt', err => { if (err) { console.error(err); return; } console.log('success!'); }); // Promise fse.copy('./test.txt', './world/test.txt').then(() => { console.log('success'); }).catch(e => { console.error(e); }); // Async/Await const copyFile = async () => { try { await fse.copy('./test.txt', './world/test.txt'); console.log('success'); } catch (e) { console.error(e); } }; copyFile()fs-extra提供了很多遍历的API,这些API都有异步和同步的两个版本,简单列举一部分异步的API,详细的API文档看这里。
fse.copy(src: string, dest: string, [options: object, callback: func]) // 复制文件或目录,目录可以包含内容 fse.emptyDir(dir: string, [callback: function]) // 确保目录为空,如果目录不为空,则删除目录下内容,如果目录不存在,则创建该目录 fse.ensureFile(file: string, [callback: func]) // 确保文件存在。如果请求创建的文件位于不存在的目录中,则会创建这些目录。如果该文件已存在,则不进行修改。(别名:createFile) fse.ensureDir(dir: string, [callback: func]) // 确保目录存在,如果目录结构不存在,则创建它,如果目录存在则不进行操作 fse.ensureLink(srcpath: string, dstpath: string, [callback: func]) // 确保链接存在。如果目录结构不存在,则创建它。 fse.move(src: string, dest: string, [options: object, callback: func]) // 移动文件或目录 fse.outputFile(file: stirng, data: string|Buffer|Uint8Array, [options: string|object, callback: func]) // 几乎与writeFile相同,除了如果父目录不存在则创建它,file必须是文件路径(不允许使用缓存区或文件描述符) fse.outputJson(file: string, object: object, [options: object, callback: func]) // 将对象写入JSON文件,几乎与writeJSON相同,除了如果目录不存在,则创建它 fse.writeJson(file, object, [options, callback]) // 将对象写入JSON文件,几乎与outputJSON相同,除了必须保证目录存在之外 fse.pathExists(file: string [, callback: func]); // 检查文件系统来测试给定路径是否存在 fse.readJson(file: string, [options: object, callback: func]) // 读取JSON文件,并将其解析为对象 fse.remove(path: string, [callback: func]) // 删除文件或者目录,该目录可以包含内容,类似rm -rf