Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

node基础篇之文件操作 #9

Open
kekobin opened this issue Jul 21, 2019 · 0 comments
Open

node基础篇之文件操作 #9

kekobin opened this issue Jul 21, 2019 · 0 comments

Comments

@kekobin
Copy link
Owner

kekobin commented Jul 21, 2019

写在前面

node中的文件操作算是非常频繁的了,它有非常多的api提供了对文件及文件夹的各种操作。下面通过对常见的api的案例讲解,来了解它们的具体用法。

一 读取文件 fs.readFile()、fs.readFileSync()

fs文件操作基本都包含同步和异步两种方式,后面都会同时对每种功能提供这两种方式的讲述,不在赘述。

异步 fs.readFile(文件名,[可选参数,编码方式,如'utf-8'],callback)

fs.readFile('/Users/kekobin/node-dir-test/util.js', (err, buffer) => {
    if(err) throw err;
    console.log(buffer.toString())
})   

callback回调包含err和buffer,默认不传编码方式,返回的是buffer二进制数据,不过可以通过

buffer.toString()

转为字符串。之所以默认使用buffer,是因为二进制流占用内存小,传输和运算快,性能高。

同步 fs.readFileSync(文件名,[可选参数,编码方式,如'utf-8'])

try {
    const buffer = fs.readFileSync('/Users/kekobin/node-dir-test/util.js')
} catch(e) {}

fs.readFile()、fs.readFileSync()在读取文件时,会不断的把读取的内容缓冲到内存中,直到缓冲完整个文件,所以对于大量或者大文件的读取时,一般使用fs.createReadStream() 进行流式传输替代,减少内存的压力。

二 写入文件 fs.writeFile() fs.writeFileSync()

异步 fs.writeFile(文件名,写入的内容,[可选参数,编码方式,如'utf-8'],callback)

fs.writeFile('/Users/kekobin/node-dir-test/util.js','test writing file', (err) => {
    if(err) { console.log('写入文件失败');return; }
    console.log('写入文件成功');
})   

callback回调只有err参数,表示写入是否成功。

同步 fs.writeFileSync(文件名,写入的内容,[可选参数,编码方式,如'utf-8'])

try {
    const err = fs.writeFileSync('/Users/kekobin/node-dir-test/util.js','test writing file')   
} catch(e) {}

三 创建文件夹 fs.mkdir() fs.mkdirSync()

异步 fs.mkdir(文件名,权限,callback)

fs.mkdir('/Users/kekobin/node-dir-test/test-dir','0777', (err) => {
    if(err) { console.log('创建文件夹失败');return; }
    console.log('创建文件夹成功');
})   

"权限"指的是被创建的文件夹是否可读、可写等。如'0777'指的是root:root权限,'0750'指的是www-data:www-data权限(不过一般它也用'0755').

drwxr-x---: '0750'; drwxr-x--x: '0755';

同步 fs.mkdirSync(文件名,权限)

try {
    const err = fs.mkdirSync(''/Users/kekobin/node-dir-test/test-dir',0777)   
} catch(e) {}

四 获取已存在的文件或文件夹状态 fs.stat() fs.statSync()

往往通过这两个api判断正在处理的是文件,还是文件夹

异步 fs.stat(文件名或者文件夹名)

fs.stat('/Users/kekobin/node-dir-test/test-dir',(err, stats) => {
    if(err) throw err;
    if(stats.isFile()) {}
    if(stats.isDirectory()) {}
})   

回调中参数stats是一个包含文件或者文件夹信息的对象,最常用的是使用stats.isFile()判断是否文件,使用stats.isDirectory()判断是否文件夹。

同步 fs.statSync(文件名或者文件夹名)

try {
    const stats = fs.statSync('/Users/kekobin/node-dir-test/test-dir')   
} catch(e) {}

由于fs.exists()已经废弃了,所以判断文件或者文件夹是否存在,也可以通过这两个api判断,即读取一个文件夹,判断stats.isDirectory()是否为true,true说明存在,否则不存在。

五 读取目录 readdir(),readdirSync()

这两个api会返回一个所包含的文件和子目录的数组。

异步 fs.readdir(文件夹名)

const dir = '/Users/kekobin/node-dir-test/';
fs.readdir(dir, function (err, files) {
    if (err) {
      throw err;
      return;
    }

    files.forEach( (filename, index) => {
        const fullname = path.join(dir,filename);
        fs.stat(fullname, (err, stats) => {
            if(err) throw err;
            if(stats.isDirectory()) {

            }
            if(stats.isFile()) {
            }
        })
    });
});  

一般用于遍历文件夹,生成文件树等操作。

同步 fs.readdirSync(文件夹名)

try {
    const files = fs.readdirSync(dir)   
} catch(e) {}

六 创建文件读取流 fs.createReadStream(文件名)

往往用于打开大型的文本文件,创建一个读取操作的数据流。所谓大型文本文件,指的是文本文件的体积很大,读取操作的缓存装不下,只能分成几次发送,每次发送会触发一个data事件,发送结束会触发end事件。

let result = '';
fs.createReadStream('/Users/kekobin/node-dir-test/util.js')
.on('data', (data) => {
    result += data;
})
.on('end', () => {
    console.log('获取最终文件读取的内容', result);
})

七 创建文件写入流 fs.createWriteStream(文件名)

创建一个写入数据流对象,该对象的write方法用于写入数据,end方法用于结束写入操作。

const 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();
    });
}
// 将util.js拷贝到util2.js
fileCopy('/Users/kekobin/node-dir-test/util.js', '/Users/kekobin/node-dir-test/util2.js', function() {
    console.log('end')
})

八 删除文件 fs.unlink() fs.unlinkSync()

异步 fs.unlink(文件名)

fs.unlink('/Users/kekobin/node-dir-test/util2.js', function(err){
    if(err) throw err;
    console.log('文件删除成功');
}); 

同步 fs.unlinkSync(文件名)

try { fs.unlinkSync('/Users/kekobin/node-dir-test/util2.js') } catch(){}

九 删除目录 fs.rmdir() fs.rmdirSync()

异步 fs.rmdir(文件夹名,callback)

fs.rmdir('/Users/kekobin/node-dir-test/test-dir', function(err){
    if(err) throw err;
    console.log('目录删除成功');
}); 

同步 fs.rmdirSync(文件名)

try { fs.rmdirSync('/Users/kekobin/node-dir-test/test-dir') } catch(){}

不过,该方法只能删除空目录,不为空的是删除不了的。后者使用下面的方式删除:

var deleteFolderRecursive = function(path) {
  if( fs.existsSync(path) ) {
    fs.readdirSync(path).forEach(function(file,index){
      var curPath = path + "/" + file;
      if(fs.lstatSync(curPath).isDirectory()) { // recurse
        deleteFolderRecursive(curPath);
      } else { // delete file
        fs.unlinkSync(curPath);
      }
    });
    fs.rmdirSync(path);
  }
};
view raw

十 检查路径是否存在 fs.existsSync(path)

if(fs.existsSync(path)){}

该方法如果存在返回true,否则返回false,不会像fs.statsSync那样在文件不存在时报错

实例:同步读取某个目录下所有文件

const getFiles = function(dir){
    const results = [];
    const files = fs.readdirSync(dir, 'utf8');

    files.forEach(function(file){
        const fullname = path.resolve(dir, file);
        const stats = fs.statSync(fullname);

        if(stats.isFile()){
            results.push(fullname);
        }else if(stats.isDirectory()){
            results = results.concat( getFiles(fullname) );
        }
    });

    return results;
};

const files = getFiles('/Users/kekobin/node-dir-test/');

realpathSync realpath

获取文件的真实绝对路径

image

大文件读取

先使用程序得到一个大文件:

const fs = require('fs');
const file = fs.createWriteStream('./big.file');
for(let  i = 0;i<=1e6;i++) {
    file.write('Lorem ipsum dolor sit amet, consectetur adipisicing elit. \n');
}
file.end();

image

然后读取它:

const fs = require('fs');
const server = require('http').createServer();
server.on('request', (req, res) => {
    fs.readFile('./big.file', (err, data) => {
        if(err) throw err;
        res.end(data);
    })
});
server.listen(8000);

我们可以先看看这个进程的内存占用情况:
image

然后访问下 curl 127.0.0.1:8000 看看现在的内存占用情况:
image

很明显,通过fs.readFile 是将文件内容直接读取到内从中,小文件还行,大文件可能会将内容撑爆掉。大文件一般是使用一行一行读取的形式,node给出的方案是 readInline,这个就不深究了。

参考

文件读取
nodejs流-你需要知道的一切

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant