在node我们如果操作文件,就需要读取buffer.通过fs读取
缓存 Buffer 你知道多少
文件操作,流的操作都要用到buffer,
一个字节最大多少? 假设8个1用10进制表示 265,他们分别有一下几个特点:
2进制 | 8进制 | 10进制 | 16进制 |
---|---|---|---|
0b开头 | 0o开头 | 278 | 0x开头 |
- 关于进制的转换 16 进制 转换为 10 进制
- 关于10进制 小于127的我们认为是单字节,大于127的我们认为是汉字和双字节
- 将10进制转换为任意进制
(278).tostring(16)
=> 116
比如 unicode编码都对应着一个utf8
规则
十六进制 | utf8编码方式 |
---|---|
0000 0000-0000 007f | 0xxxxxxx |
0000 0080-0000 07ff | 110xxxxx 10xxxxxx |
0000 0800-0000 ffff | 1110xxxx 10xxxxxx 10xxxxxx |
0001 0000-0000 ffff | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
如果是汉字,是3个字节,那就是第三行。对于英语字母,是两个字节,和 ASCLL是相同的,我们区分他们用的就是编码前面的标识。
比如我们要转换一个 16进制编码,代码如下
function transfer(r){
let code = [1110,10,10]; //汉字三个字节,拼接字符串
code[2] += (r).toString(2).slice(-6);
code[1] += (r).toString(2).slice(-12,-6);
code[0] += ((r).toString(2).slice(0,-12)).padStart(4,0);
code = code.map((item)=>parseInt(item,2)) ; //将二进制转换回十进制
return Buffer.from(code).toString() //转换成buffer在转换成汉字
}
node要操作文件都是二进制,声明一段内存,有两个方法:buffer.from
,buffer.alloc
;
buffer将二进制转换为16进制展示,非分配内存的大小不能更改,比如v8 1.7g(64) 0.9g(32),内存是属于非引用空间,但是属于引用类型,非常像二维数组,buffer.alloc
会输出 <Buffer 00 00 00 00 00 00>
,安全,速度慢,通过长度创建,buffer.from
通过十进制的数组或者字符串,node不支持gb2312 格式,但是当我们爬虫一个这种类型的网站的时候,可以用一个包(icon-lite),使用之前需要安装,使用方法,请参考www.npmjs.com/package/ico…
let fs = require('fs');
let path = require('path');
let iconvlite = require('iconv-lite'); //这个包需要转化的是buffer
let r = fs.readFileSync(path.resolve(__dirname,'a.txt'));
let result = iconvlite.decode(r,'gbk')//可以转换成任意格式
console.log(result)
由于buffer实际上是二进制,那么他就可以用toString
进行转换
let buffer = Buffer.from('走开')
console.log(buffer.slice(0,2).toString())
console.log(buffer.slice(2,6).toString())
=> �
�开
上面这种情况如何解决呢?也是一个内置方法
let buffer = Buffer.from('走开')
let a = buffer.slice(0,2);
let b = buffer.slice(2,6);
let { StringDecoder } = require('string_decoder');
let sd = new StringDecoder();
console.log(sd.write(a)) //此时不是正常汉字则保留在sd的内部
console.log(sd.write(b)) //下次输出会把上次的结果一同输出
=> 走开
缓存输出,把不能拼成汉字的先缓存,在输出.
buffer的其他方法buffer.copy
,buffer.concat
,具体实现
let buffer1 = Buffer.alloc(6);
let buffer2 = Buffer.from("走开");
Buffer.prototype.mycopy = function(target,targetStart,sourceStart,sourceEnd){
for( let i = 0;i< sourceEnd - sourceStart;i++){
target[i + sourceStart] = this[sourceStart + i]
}
}
buffer2.mycopy(buffer1,1,3,6);//目标buffer, 目标开始的拷贝位置,源的开始和结束位置
console.log(buffer1.toString());
buffer2.copy(buffer1,1,3,6);
console.log(buffer1.toString());
接收请求时会采用concat
方法进行拼接
let buffer1 = Buffer.from("走");
let buffer2 = Buffer.from("开");
let buffer3 = Buffer.from("啊");
Buffer.concat([buffer1,buffer2,buffer3]);//目标buffer, 目标开始的拷贝位置,源的开始和结束位置
console.log(Buffer.concat([buffer1,buffer2,buffer3]).toString());
Buffer.myconcat = function(list,len){
//计算要生成的buffer长度,将list每一项求和
if(typeof len === 'undefined'){
len = list.reduce((current,next,index) => {
return current + next.length
},0)
}
let newBuffer = Buffer.alloc(len);
let index = 0;
list.forEach(buffer => {
buffer.copy(newBuffer,index)
index += buffer.length;
});
return newBuffer.slice(0,index)
}
Buffer.myconcat([buffer1,buffer2,buffer3]);//目标buffer, 目标开始的拷贝位置,源的开始和结束位置
console.log(Buffer.myconcat([buffer1,buffer2,buffer3]).toString());
类似还有indexof,下面我们在原型上扩展一个split方法
let buffer1 = Buffer.from("走**走**走");
Buffer.prototype.split = function(sep){
let index = 0;
let len = Buffer.from(sep).length;//查找buffer的长度
let i = 0;
let arr = []
while( -1 != (i = this.indexOf(sep,index))){
let a = this.slice(index,i);
index = i + len;
arr.push(a)
}
arr.push(this.slice(index))
return arr.map(item => item.toString()));
}
buffer1.split('**');
fs方法有哪些
- readFile/ writeFile /copyFile
- read/write/open/sync/colse
- fs.mkdir /rmdir/rname/readfir
- ...
fs模块, 在nodejs中,使用fs模块来实现所有有关文件及目录创建,写入删除操作,所有方法氛围同步异步两种实现,带sync为同步,反之异步,在此之前我们先知道权限的问题
linux权限
文件类型与权限 | 链接占用的节点 | 文件所有者 | 文件所有者的用户组 | 文件大小 | 文件创建的事件 | 最近修改时间 | 文件名称 |
---|---|---|---|---|---|---|---|
-rw-r--r-- | 1 | root | root | 34298 | 04-02 | 00:23 | install.log |
-
代表当前是文件,d
代表目录,r
代表读w
代表写,`````
权限项 | 读 | 写 | 执行 | 读 | 写 | 执行 | 读 | 写 | 执行 |
---|---|---|---|---|---|---|---|---|---|
字符表示 | r | w | x | r | w | x | r | w | x |
数字表示 | 4 | 2 | 1 | 4 | 2 | 1 | 4 | 2 | 1 |
权限分配 | 文件所有者 | 文件所有组 | 其他用户 |
他们三个组成权限位:二爷一直死读书
//文件中存的永远是二进制
let fs = require('fs');
fs.readFile('1.txt',{encoding:'utf8',flag:'r'},function(err,data){
if(err) return console.log(err);
console.log(data);
})
//写
fs.writeFile('a.txt',Buffer.from('123'),{flag:'',mode:0o444},function(err,data){
if(err) return console.log(err);
console.log("写入成功");
})
//copy
fs.copyFile('a.txt','3.txt',function(err,data){
console.log("拷贝成功");
})
flag
- w+ 读取并写入,如果存在则清空文件内容,不存在则创建内容
- r+ 读取并写入,文件不存在则报错
- r 文件不存在则报错
- w 文件不存在则创建,存在则清空
fs文件操作
fs文件操作 - read
let fs = require('fs');
//fd文件描述符,符号从3开始, 0 标准输入,1标准输出 2代表错误输出
fs.open('1.txt','r',function(err,fd){
//把文件中的内容读取到buffer中,offsetbuffer是buffer的偏移量
let BFFER_SIZE = 3;
let buffer = Buffer.alloc(3);//读取到哪个buffer上
//将fd的内容读到buffer里,从第0开始读,读BFFER_SIZE个,从文件的第0 个位置开始读,成功以后回调,byteRead实际读到的个数
// fs.read(fd,buffer,0,BFFER_SIZE,0,function(err,byteRead){
// fs.read(fd,buffer,0,BFFER_SIZE,0,function(err,byteRead){
// fs.read(fd,buffer,0,BFFER_SIZE,0,function(err,byteRead){
// })
// })
// })
//等同于
let index = 0;
function next(){
fs.read(fd,buffer,0,BFFER_SIZE,index,function(err,byteRead){
index += byteRead;
if(byteRead == BFFER_SIZE){
next()
}else{
fs.close(fd,()=>{
console.log('close')
})
}
console.log(buffer.slice(0,byteRead).toString())
})
};
next();
})
fs文件操作 - copy
let fs = require('fs');
function copy(source,target){
let index = 0;
let buffer = Buffer.alloc(3);
let BUFFER_SIZE = 3;
fs.open(source,'r',function(err,rfd){//开启读取文件描述符
if(err) return console.log(err);
fs.open(target,'w',0o666,function(err,wfd){ // 开启写入描述符
function next(){
fs.read(rfd,buffer,0,BUFFER_SIZE,index,function(err,byteRead){
fs.write(wfd,buffer,0,byteRead,index,function(err,byteWritten){
index += byteRead;
if(byteWritten){//如果有写入内容,就继续读取
next();
}else{
fs.close(rfd,()=>{});
//吧内存中的内容强制写入在关闭文件,以为写入是异步操作
fs.fsync(function(){
fs.close(wfd,()=>{});
})
}
})
})
}
next();
})
})
}
copy('5.txt','6.txt')
如果你报错binding.fsync(fd, req);
^
TypeError: fd must be a file descriptor
fs.write这个方法在执行的时候会检测该文件是否已经存在了,如果已经存在便会报这个错误,解决办法就是先删掉本地该文件,然后再执行就成功了,如下所示。 详情请参考https://blog.csdn.net/u012453843/article/details/60958779
当然以上这么麻烦,于是我们有流,流提供了copy的功能,pipe
有关流清参考文章
目录相关,树的遍历 fs.mkdir /rmdir/rname/readfir
fs主要做fileSystem,我们可以通过他监听文件的变化,判断文件的状态,文件的读写还有目录的操作等等
- 创建目录
let fs = require('fs');
//创建目录,同步方法和异步方法,如果只读一次建议使用同步,同步编写比较容易
//异步不会阻塞主线程,性能高一些
//fs.mkdirSync('a/b') //只能创建目录,不能创建js文件,不能跨级创建,必须保证父级存在
function makep(dir,callback){
let dirs = dir.split('/');
let index = 1;
function next(index){
//当索引溢出时 就不要递归了
if(index === dir.length + 1 ) return callback;
let p = dirs.slice(0,index).join('/');
fs.access(p,(err) => {
if(!err){
next(index+1);
}else{
//如果没有这个文件就会走到err中,创建这个文件,创建完毕后创建下一个文件
fs.mkdirp(p,(err) => {
if(err) return console.log(err);
next(index + 1)
})
}
})
}
next(index)
}
makep('a/b/c/d')
- 删除目录
当我们删除文件夹的时候fs.rmdirSync('a');
有时候会报错误directory not empty
,也就是说删除文件夹必须保证a目录是空的,此时我们需要对文件夹进行遍历,
遍历分先序,中序,后序,(删除目录一般是先序)或者深度和广度 ;
先写一个同步一层的
let fs = require('fs');
let path = require('path');
//只能读儿子目录
let dirs = fs.readdirSync('c');
dirs = dirs.map(item => path.join('c',item))
dirs.forEach(p =>{
let stat = fs.statSync(p);
console.log('atat' + stat.isDirectory())
if(stat.isDirectory()){//是否是文件夹
fs.rmdirSync(p)//删除目录
}else{
fs.unlinkSync(p) //删除文件
}
})
fs.rmdirSync('c')//删除自己
多层删除,先序深度
let fs = require('fs');
let path = require('path');
//同步删除文件夹
function removeDirSync(dir){
//允许人家删除的不一定是目录
let stat = fs.statSync(dir);
if(stat.isDirectory()){//是否是文件夹
let dirs = fs.readdirSync(dir);
dirs = dirs.map(item => path.join(dir,item))
dirs.forEach(d =>{
removeDirSync(d)
})
fs.rmdirSync(dir)//最后删除自己
}else{
fs.unlinkSync(dir) //删除文件
}
}
removeDirSync('c')
异步删除文件夹
let fs = require('fs');
let path = require('path');
//异步删除文件夹 promise
function removeDir(dir){
return new Promise((resolve,reject) => {
fs.stat(dir,(err,stat)=> {//第二个参数返回的是之前let stat
if(stat.isDirectory()){//是否是文件夹
let dirs = fs.readdir(dir,(err,dirs)=>{
dirs = dirs.map(item => path.join(dir,item));
dirs = dirs.map(p => removeDir(p)); //保证返回的是promise
//当两个方法同时完成的时候
Promise.all(dirs).then(()=>{
fs.rmdir(dir,resolve) //删除自己
})
});
}else{
fs.unlink(dir, resolve) //删除文件
}
})
})
}
removeDir('c').then(data =>{
console.log('成功')
})
//异步删除文件夹
let fs = require('fs');
let path = require('path');
//异步删除文件夹 promise
function rmdir(dir,callback){
fs.stat(dir,(err,stat)=> {//第二个参数返回的是之前let stat
if(stat.isDirectory()){//是否是文件夹
let dirs = fs.readdir(dir,(err,dirs)=>{
//只要涉及到异步递归就用next
function next(index){
if(dirs.length === 0 ||(index == dirs.length)) {
return fs.rmdir(dir,callback)
}
let p = path.join(dir,dirs[index]);
rmdir(p,() => next(index+1));
}
next(0);
});
}else{
fs.unlink(dir,callback) //删除文件
}
})
}
rmdir('a',() =>{
console.log('delecte ok')
})
广度删除
let fs = require('fs');
let path = require('path');
//异步删除文件夹 promise
function preWide(dir){
let arr = [dir];
let index = 0;
while(arr[index]){
let current = arr[index++];
let stat = fs.statSync(current);
if(stat.isDirectory()){
let dirs = fs.readdirSync(current);
arr = [...arr,...dirs.map(d => path.join(current,d))]
}
}
for(var i = arr.length-1;i >= 0;i--){
let p = arr[i];
let stat = fs.statSync(p);
if(stat.isDirectory()){
fs.rmdirSync(p)
}else{
fs.unlinkSync(p)
}
}
}
preWide("a");
广度异步删除 promise 如果有人有更好的方法的话,麻烦提供代码,谢谢,这里是我多次修改的全部代码
let fs = require('fs');
let path = require('path');
let arr = [];
let index = 0;
function preWideDir(dir){
if(arr.length === 0){arr[0] = dir}
let current = dir;
return new Promise((resolve,reject) => {
stat = fs.stat(current,(err,stat) =>{ //判断当前目录状态 异步操作
if(stat.isDirectory()){ //如果是文件夹
let dirs = fs.readdir(current,(err,dirs)=>{ //读取文件夹的内容
dirs = dirs.map(d => path.join(current,d));
arr = [...arr,...dirs];
if(index < arr.length-1){
index++;
preWideDir(arr[index])
}else{
for(var i = arr.length-1;i >= 0;i--){
let p = arr[i];
let stat = fs.stat(p,(err,stat) =>{
if(stat.isDirectory()){
fs.rmdir(p,resolve)
}else{
fs.unlink(p, resolve)
}
});
}
}
})
}else{
if(index < arr.length-1){
index++;
preWideDir(arr[index])
}else{
for(var i = arr.length-1;i >= 0;i--){
let p = arr[i];
let stat = fs.stat(p,(err,stat) =>{
if(stat.isDirectory()){
fs.rmdir(p,resolve)
}else{
fs.unlink(p, resolve)
}
});
}
}
}
})
})
}
preWideDir("src/b.8").then(data=>{
console.log("删除成功")
})
广度异步删除 promise
let fs = require('fs');
let path = require('path');
let arr = [];
let index = 0;
function preWideDir(dir){
if(arr.length === 0){arr[0] = dir}
let current = dir;
return new Promise((resolve,reject) => {
fs.stat(current,(err,stat) =>{ //判断当前目录状态 异步操作
index++;
if(stat.isDirectory()){ //如果是文件夹
fs.readdir(current,(err,dirs)=>{ //读取文件夹的内容
dirs = dirs.map(d => path.join(current,d));
arr = [...arr,...dirs];
dirs = dirs.map(p => preWideDir(p));
if(index == arr.length-1){
for(let i = arr.length -1;i >= 0;i--){
let p = arr[i];
console.log(arr)
fs.stat(p,(err,stat) =>{
if(stat.isDirectory()){
console.log(p + stat.isDirectory())
fs.rmdir(p,resolve)
}else{
fs.unlink(p,resolve)
}
});
}
}
})
fs.rmdir(dir,resolve)
}
})
})
}
preWideDir("src/a.7").then(data=>{
console.log("删除成功")
})
let fs = require('fs');
let path = require('path');
let arr =[];
function preWideDir(dir){
if(!arr[dir]) arr.push(dir);
console.log(dir)
return new Promise((resolve,reject) => {
fs.stat(dir,(err,stat) =>{ //判断当前目录状态 异步操作
if(stat.isDirectory()){ //如果是文件夹
fs.readdir(dir,(err,dirs)=>{ //读取文件夹的内容
dirs = dirs.map(d => path.join(dir,d));
arr = [...arr,...dirs];
dirs = dirs.map(p => preWideDir(p));
Promise.all(dirs).then(()=>{
for(let i = arr.length - 1;i >= 0;i--){
let p = arr[i];
let stat =
fs.stat(p,(err,stat) =>{
if (err) {
console.log("找不到文件"+p);
return;
}
if(stat.isDirectory()){
fs.rmdir(p,resolve)
}else{
fs.unlink(p,resolve)
}
});
}
})
})
}else{
resolve();
}
})
})
}
preWideDir("a.4").then(data=>{
console.log("删除成功")
})
广度异步删除
let fs = require('fs');
let path = require('path');
let arr =[];
function prew(dir,callback){
let arr = [dir];
function next(index){
if(arr[index]){
fs.stat(arr[index],(err,stat) =>{
if(err) {
return;
};
if(stat.isDirectory()){
fs.readdir(arr[index],(err,dirs)=>{ //读取文件夹的内容
if(dirs.length === 0){
next(++index);
return;
}
arr = [...arr,...dirs.map(d => path.join(arr[index],d)) ];
console.log(arr.length)
next(++index);
})
}else{
next(++index);
}
})
}else{
for(let i = arr.length - 1;i >= 0;i--){
let p = arr[i];
fs.stat(p,(err,stat) =>{
if (err) {
console.log("找不到文件"+p);
return;
}
if(stat.isDirectory()){
fs.rmdir(p,null)
}else{
fs.unlink(p,null)
}
});
if(i == 0){callback()}
}
}
}
next(0);
}
prew("a.22" ,()=>{
console.log("删除成功")
})