node基础篇之文件操作
Opened this issue · 0 comments
写在前面
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
获取文件的真实绝对路径
大文件读取
先使用程序得到一个大文件:
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();
然后读取它:
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);
然后访问下 curl 127.0.0.1:8000 看看现在的内存占用情况:
很明显,通过fs.readFile 是将文件内容直接读取到内从中,小文件还行,大文件可能会将内容撑爆掉。大文件一般是使用一行一行读取的形式,node给出的方案是 readInline,这个就不深究了。