第三章 文件系统
第一讲.Buffer缓冲区
从结构上看buffer很像一个数组,操作的方法也和数组类似.
数组中不能存储二进制数据,但是buffer可以,它的元素为16进制的两位数
使用buffer不需要引入模块,直接使用即可
var str="hello";
var buf=buffer.from(str);//将字符串内容转换成十六进制存储
buffer中的每一个元素的范围都是00-ff,实际上一个元素就表示内存中的一个字节
8bit=1byte
一个汉字占2个长度单位,3个字节空间
buffer中的内存不是通过JS分配的,而是直接在底层通过C++来创建的
也就是我们可以直接通过buffer来创建内存中的空间
以前创建指定大小的buffer:
var buf=new Buffer(10);//创建10字节大小的buffer
buffer的所有构造函数都不推荐我们使用,已经过期(上面那个不能用了)
现在创建buffer:
var buf=Buffer.alloc(10);
buf[0]=0xaa;//添加一个十六进制字符
buf[1]=88;//添加一个十进制字符
buf[10]=0xff;//会被忽略
buf[6]=556;//溢出了,只取后8位
Buffer的大小一旦确定,永远不能修改,Buffer是对底层内存的直接操控,其中的空间是连续的
如果下标越界,越界的部分会被忽略
只要Buffer中的数字在控制台或页面中输出,都会自动转换成十进制
如果想输出原有内容出来,可以将其转换为字符串:
console.log(buf[1].toString(16));//16指的是以16进制输出
当使用Buffer.alloc( )时,使用内存空间之前,会将内存中的内容全部清空
但是如果使用Buffer.allocUnsafe( ),就不会清空其中的内容,因此buffer中可能会有敏感数据
但是Unsafe方法的性能会更好一些
buf.toString( )方法可以将缓冲区中的内容直接转换成字符串
第二讲.文件系统简介
文件系统(File System,简称fs),通过node操作系统文件
在Node中,与文件系统的交互是非常重要的,服务器的本质就是将本地的文件发送给远程的服务端
Node通过fs模块和文件系统进行交互
该模块中提供了一些标准文件来打开,读取,写入文件,以及与其交互
使用fs需要先引fs模块,直接引入不需要下载:
var fs=require('fs');
fs模块中所有方法都有两种形式可以选择: 同步和异步
同步文件系统会阻塞程序的执行,也就是除非操作完毕,否则不会向下执行代码
异步文件不会阻塞程序的执行,而是在操作完成时,通过回调函数将结果返回
异步方法都有回调函数,同步方法都有返回值
第三讲.同步文件写入
手动写入文件的步骤:
打开文件
向文件中写入内容
保存并关闭文件
同步文件的写入:
打开文件
三个参数:
path : 要打开文件的路径
flags : 打开文件要做的操作类型(读还是写) r 只读 w 可写
mode : 设置文件的操作权限,一般不传
会返回一个描述符(文件编号)作为结果,我们可以对该描述符进行各种操作
var fd=fs.openSync('hello.txt','w');
向文件中写入内容
参数:
fd : 要写入的文件的描述符
string : 要写入的内容
position : 文件写入的起始位置(一般不传)
fs.writeSync(fd,'今天天气真不错');
保存并关闭文件
在服务器中,程序不会停止,因此文件需要单独手动关闭
参数: fd 要关闭的文件的描述符
fs.closeSync(fd);
第四讲.异步文件写入
异步方法没有返回值,由回调函数的参数返回
回调函数有两个参数:
err : 错误对象,如果没有错误则为null
如果使用只读打开文件,但是又没有创建文件,就会报错
fd : 文件的描述符
异步写入的特点是不阻塞其他进程,当文件读取以后,接下来的操作会交给线程池来进行,由线程池判断谁先谁后
因此,open下面的代码可能会先于open的回调函数执行.
因此,对文件的写入操作应该写在回调函数中
异步文件写入步骤:
引入fs模块
var fs=require("fs")
打开文件
方法 : fs.open( )
参数:
path : 文件路径
flags : 操作标志
mode : 通常省略
callback : 回调函数 不能省略
fs.open('hello.txt','w',function(err,fd){
if(!err){
console.log(fd);
}else{
console.log(err);
})
写入内容
方法 : fs.write( )
参数:
fd : 文件描述符
text : 写入的内容
callback : 回调函数
参数只有一个,就是err
fs.open('hello.txt','w',function(err,fd){
if(!err){
fs.write(fd,'今天天气真不错',function(err){
if(!err) console.log('写入成功');
})
}
})
保存并关闭文件
只要回调函数中的内容完成之后,就可以关闭文件
方法 : fs.close( )
参数: fd callback
回调函数的参数同样只有err
fs.open('hello.txt','w',function(err,fd){
if(!err){
fs.write(fd,'今天天气真不错',function(err){
if(!err) console.log('写入成功');
})
}
fs.close(fd,function(err){});
})
异步的优点:
程序会继续执行,不必等待系统响应
但是同步更符合人类思维,便于编程
而异步不符合人类函数,代码可读性差,但是有性能优势
异步可以处理异常,而同步不能处理异常
第五讲.简单文件写入
不论是同步还是异步,都比较麻烦,在实际开发中用的不多
也分简单同步和简单异步
简单同步 : fs.writeFileSync(file,data,[option]);
简单异步 : fs.writeFile(file,data,[option],callback);
参数:
file : 要操作的文件路径
data : 要写入的数据
option : 写入的设置,可选参数,一般不写
以对象形式传入,可选参数有 : 编码形式,mode,操作权限
{
flag:'a';
}
callback : 回调函数
回调函数的参数 : err
写入步骤:以异步为例
引入fs模块
var fs=require('fs');
写入
fs.writeFile('hello.txt','今天天气真不戳',function(err){
if(!err) console.log('写入成功');
})
它会自动打开和关闭
文件写入时,默认是从头开始写,会覆盖曾经写过的内容
如果不想从头写,要将flags写成 a ,意为追加
之前写的是相对路径,但是也可以写绝对路径,不过由于’\'是转义字符,因此要写双斜杠代替斜杠才能表示路径:
fs.writeFile("C:\\User\\Desktop\\hello.txt",'今天天气真不戳',{flag:'a'},function(err){
if(!err) console.log('写入成功');
})
或者,不写\,而是写/,可以只写一个,也可以达到相同的效果
第六讲.流式文件写入
之前写入文件的方式,都是已经准备好了所有要写的内容,然后一次性写入
因此必须在写之前准备好所有文件,这样会存在一个问题,如果文件过大,会导致占用过多的内存空间
因此,前面所有的文件写入,都不适合大文件的写入,性能较差,且容易导致内存溢出
由此引入了流式文件写入,相当于在文件上通了一根水管,源源不断的将文件送进去,而不是一次性写入
步骤:
打开文件
var fs=require('fs');
创建一个可写流(就是一个水管)
方法 : fs.createWriteStream(path,[option])
参数 : 写入文件的路径,写入设置
var ws=fs.createWriteStream('hello.txt');
这样ws就相当于一根水管,hello.txt就相当于一个水池,二者成功连接上
像文件中输出内容
方法 : ws.write( )
参数 : 字符串
ws.write('今天天气真不戳');
它的特点是只要ws还在,想什么时候写就什么时候写.
但是目前还没有关闭,可能对性能有所损伤
可以通过监听流的open和close事件来监听流的打开和关闭
监听方法 : ws.once( )
ws.on( ) 和 ws.once( )都可以绑定一个事件,不同的是on绑定的是长期有效的事件,而once绑定的是一次性事件
on是事件每次触发都会执行方法,once是只有第一次触发会执行方法
由于open只会出现一次,所以这里用once比较好
参数:
要监听的事件
回调函数
ws.once('open',function(){
console.log('流打开了');
})
流式写入是异步写入,在关闭流时,不能从接受端断开,因为还会有内容在流中没有传输完成,因此不能使用ws.colse( )方法
但是可以从发送端断开,这样流中的文件可以正常传输
从发送端断开的方法 : ws.end( )
ws.end();
第七讲.简单文件读取
文件的读取和写入类似,同样有同步文件读取,异步文件读取,这两个不再讲
另外就是简单文件读取和流式文件读取
简单文件读取方法 :
同步简单读取 : fs.readFileSync(path,[option])
异步简单读取 : fs.readFile(path,[option],callback)
参数:
path : 要读取文件的路径
potion : 要读取文件的设置
callback : 回调函数
参数:
err : 异常
data : 读取到的数据,返回类型为Buffer,需要toString( )一下才能用
返回值是Buffer,目的是在读取图片,音频等非文本文件时,也可以正常读取,而不是出现乱码
步骤:
打开文件
var fs=require('fs');
读取文件
fs.readFile('hello.text',function(err,data){
if(!err) console.log(data.toString());
})
在读取数据之后,还可以再写出去
写入文件
fs.readFile('an.jpg',function(err,data){
if(!err){
fs.writeFile('hello.jpg',data,function(err){
if(!err) console.log('文件写入成功');
})
}
})
读取+写入,即可完成一个复制的功能.
第八讲.流式文件读取
与流式写入类似,同样适用于大文件,可以分多次读取到内存中
步骤 :
引入文件
var fs=require('fs');
创建一个可读流
方法 : fs.createReadStream(path,[option])
参数 : 读取文件的路径,读取设置
var rs=fs.createReadStream('an.jpg');
1
监听流的开启和关闭
rs.once('open',function(){
console.log('可读流打开了');
})
rs.once('close',function(){
console.log('可读流关闭了');
})
如果要读取一个可读流的数据,则必须为可读流绑定一个data事件,data事件绑定完毕,会自动开始读取数据
读取数据
读取到的数据通过data参数接收
rs.on('data',function(data){
console.log(data);
})rs.on('data',function(data){
console.log(data);
})
会自动判断文件的大小,然后分次读取
读完了会自动关闭,但是如果不读就不会关闭.
写入数据
先监听可写流的开启和关闭.
rs.on('data',function(data){
ws.write(data);
})
rs.once('close',function(){
console.log('可读流关闭了');
ws.end();//在数据读取完成之后,可读流先关闭,然后才关闭可写流
})
问题:要给读取和写入都设置监听,过于复杂
在rs中有一个方法 : rs.pipe(ws)
它可以将可读流中的方法直接写到可写流之中
当读写结束之后,会自动关闭,可以不用监听.
var fs=require('fs');
var rs=fs.createReadStream('an.jpg');
var ws=fs.createWriteStream('hello.jpg');
rs.pipe(ws);
第九讲.fs模块的其他方法
Sync表示同步,没有Sync则表示异步,每个方法只取一种进行说明
验证路径是否存在 : fs.existsSync(path,[option])
只有同步的可以用
var fs=require('fs');
var isExists=fs.existsSync('a.mp3');
返回值为true表示存在,false表示不存在
获取文件信息 : fs.stat(path,callback)
它会给我们返回一个对象stat,其中保存了当前对象状态的相关信息
stat对象中有以下内容:
isFile( )方法 是否是文件
isDirectory( )方法 是否是文件夹
size属性 文件大小
birthtime属性 文件的创建时间
var fs=require('fs');
fs.stat('a.mp3',function(err,stat){
console.log(stat.size);
})
删除文件 : fs.unlink(path,callback)
fs.unlinkSync('hello.txt');
1
会将文件删除
列出文件 : fs.readdir(path,[option],callback)
读取一个目录的目录结构
回调函数两个参数:
err 异常
files 是一个字符串数组,每一个元素就是一个文件夹或文件的名字
fs.readdir('hello.txt',function(err,files){
console.log(files);
})
截断文件 : fs.truncate(path,len,callback)
将文件修改为指定子节的大小
fs.truncateSync('hello.txt',9);
创建文件夹 : fs.mkdir(path,[mode],callback)
fs.mkdirSync('hello')
删除文件夹 : fs.rmdirSync(path)
fs.rmdirSync('hello');
重命名文件 : fs.rename(oldPath,newPath,callback)
参数:
oldPath 旧的路径(名字)
newPath 新的路径(名字)
callback 回调函数
fs.rename('a.mp3','b.mp3',function(err){
if(!err) console.log('修改成功');
})
不仅可以传递文件名,也可以传递一个新的路径,这样也可以实现文件的移动
监视文件的修改 : fs.watchFile(filename,[option],listener)
参数:
filename : 要监视的文件的名字
options : 配置选项
listener : 回调函数,当文件发生变化时,回调函数会执行
参数:
curr : 当前文件的状态
prev : 修改前文件的状态
这两个对象都是stats对象,属性和可用方法与stats相同
fs.watchFile('hello.txt',function(curr,prev){
console.log('修改前文件大小'+prev.size);
console.log('修改后文件大小'+curr.size);
})
会一直监听着这个文件,只要文件发生了变化,回调函数就会执行。