一、核心模块和对象
- 核心模块的意义
- 如果只是在服务器运行JavaScript代码,意义并不大,因为无法实现任何功能(读写文件,访问网络)。
- Node 的用处在于它本身还提供的一系列功能模块,用于与操作系统互动。
- 这些核心的功能模块在 Node 中内置。
- 常用内置模块
- path:处理文件路径
- fs:操作文件系统
- child_process:新建子进程
- util:提供一系列实用小工具
- http:提供HTTP服务器功能
- url:用于解析URL
- querystring:解析URL中的查询字符串
- crypto:提供加密和解密功能
- 其他
二、文件系统操作
- 相关模块的使用
- fs: 基础的文件操作 API
- path: 提供和路径相关的操作 API
- readline: 用于读取大文本文件,一行一行读
- fs-extra(第三方文件操作模块): https://www.npmjs.com/package/fs-extra
- path模块的使用
在文件操作的过程中,都必须使用物理路径(绝对路径),path模块提供了一系列与路径相关的 API;
// path.basename(path[, ext])
// 获取文件名(包含后缀)
console.log(path.basename(temp));
// 获取文件名(不包含后缀) 第二个参数即要删除的后缀
console.log(path.basename(temp, '.lrc'));
// path.delimiter
// 获取不同操作系统中默认的路径分隔符
// windows是分号,Linux是冒号
console.log(path.delimiter);
// 获取环境变量
console.log(process.env.PATH);
// 将对应环境变量分割
console.log(process.env.PATH.split(path.delimiter));
// path.dirname(path)
// 获取目录名称
console.log(path.dirname(temp));
// path.extname(path)
// 获取后缀名,包含点(在path.basename中就有ext)
console.log(path.extname(temp));
// path.parse(path)
// 将路径字符串转为对象(文件根目录、文件路径、文件全名(带后缀)、后缀名、文件名(不带后缀))
console.log(path.parse(temp));
// path.format(pathObject)
// 路径对象转字符串
console.log(path.format( path.parse(temp) ));
// path.isAbsolute(path)
// 判断是否为绝对路径
console.log(path.isAbsolute(temp));
// path.join([...paths])
// 路径拼接
const temp = path.join(__dirname, './lyrics/相依为命.lrc');
// path.normalize(path)
// 常规化一个路径(将多余的斜杠处理,)
var urlStr = 'C:/System32\\a//test///haha.html';
console.log(path.normalize(urlStr));
// path.relative(from, to)
// 获取to相对于from的相对路径
console.log(path.relative(__dirname, '/Users/test/Desktop/03-文件系统操作/haha.html'));
// path.resolve([...paths])
// 类似于join的字符串拼接
console.log(path.resolve(__dirname, '../', '../', 'haah.html'));
// 与join不一样的(不是简单的字符串拼接)
console.log(path.resolve(__dirname, '/User/test', 'hehe', 'haha.html')); // 输出结果是/User/test/hehe/haha.html
- 同步或异步调用
- fs模块对文件的几乎所有操作都有同步和异步两种形式
- 例如:readFile() 和 readFileSync()
- 同步调用会阻塞代码的执行,异步则不会
- 异步调用会将读取任务下达到任务队列,直到任务执行完成才会回调
- 异常处理方面,同步必须使用 try catch 方式,异步可以通过回调函数的第一个参数
- 文件读取
- 异步文件读取
fs.readFile(file[, options], callback(error, data))
- 异步文件读取
// 读取时有设置编码utf8
fs.readFile(path.join(__dirname, './test.txt'), 'utf8', (error, data)=>{
if(error) throw error;
console.log(`异步: ${data}`);
});
- 同步文件读取
fs.readFileSync(file[, options])
// 同步操作的写法,错误是通过抛出异常
try {
var data = fs.readFileSync(path.join(__dirname, './test.txt'), 'utf8');
console.log(`同步: ${data}`);
} catch (error) {
throw error;
}
node默认编码类型是utf8;
由于Windows平台下默认文件编码是GBK,在node中是不支持的gbk类型编码的!!!!
对于各种操作系统中编码问题,可以通过iconv-lite解决
- 文件流的方式读取
fs.createReadStream(path[, options])
const stream = fs.createReadStream('1.txt');
let data = ' '
stream.on('data', (trunk) => {
data += trunk;
});
stream.on('end', () => {
console.log(data);
});
流的方式Stream模块。pipe管道,可以将读取到流,导入到另外一个目标中,还可以设置流的类型;
fs.createReadStream(filename)假如该文件流数据类型是utf8 通过pipe,将流的类型改变gbk fs.createReadStream(filename).pipe(iconv.decodeStream('gbk'))
- 模块逐行读取文本
readline
const readline = require('readline');
const fs = require('fs');
const rl = readline.createInterface({input: fs.createReadStream('sample.txt') });
rl.on('line', (line) => {
console.log('Line from file:', line);
});
正则表达式: objReg.exec(string) 该函数通过对指定你的字符串进行一次匹配检测,获取字符串中的第一个与正则表达式的内容,并且将匹配的内容和子匹配的结果存放在返回数组中
var rel = /^\[\d{2}\:\d{2}\.\d{2,4}\].+$/;
var rel = /^\[(\d{2})\:(\d{2})\.(\d{2,4})\](.+)$/; // 有括号的即为子分组
rel.exec(line)
> 案例: 歌词滚动
案例: 根据歌曲时间显示对应歌词
- 文件写入
- 异步文件写入
fs.writeFile(file,data[,option],callback(err))
- 异步文件写入
fs.writeFile(path.join(__dirname, './temp.txt'), JSON.stringify({id: 10}), (err) => {
if(err){
// 一般写文件,如果出现错误,都是因为权限问题(权限不够不能创建文件)
// 文件夹如果不存在,不会创建文件夹,也会出错
console.log('err:' + err);
} else {
console.log('文件写入成功');
}
});
- 同步文件写入
fs.writeFileSync(file,data,[,option])
fs.writeFileSync(path.join(__dirname, 'temp.txt'), '你好?');
- 流式文件写
fs.createWriteStream(path[,option])
fs.createWriteStream();
var streamWriter = fs.createWriteStream(path.join(__dirname, 'temp.txt'));
// 是属于非阻塞的streamWriter.write,返回true/false
console.log( streamWriter.write('哈哈', ()=>{
console.log('+1');
}) );
默认写入操作是覆盖源文件
- pipe管道方式(文件读写操作)
var readstream = fs.createReadStream('01-path.js');
var writestream = fs.createWriteStream('test4.txt');
// 直接将读文件流 导入到 写文件流中
readstream.pipe(writestream);
- 异步追加
fs.appendFile(file,data[,options],callback(err))
fs.appendFile(path.join(__dirname, 'temp.txt'), ' 测试', (err)=>{
if(err){
console.log('error: ' + err);
} else {
console.log('追加成功');
}
});
- 同步追加
fs.appendFileSync(file,data[,options])
fs.appendFileSync(path.join(__dirname, 'temp.txt'), ' 追加成功了吗?');
- 文件其他操作
- 获取文件信息
fs.stat(path,callback(err,stats))
fs.statSync(path) // => 返回一个fs.Stats实例
- 获取文件信息
fs.stat('temp.txt', (err, stats)=>{
if(err){
console.log('err:' + err);
} else {
console.log(stats.isFile());
}
});
读取到文件信息后,可以判断文件的类型
stats.isFile()
stats.isDirectory()
stats.isBlockDevice()
stats.isCharacterDevice()
stats.isSymbolicLink()
stats.isSocket()
- 移动文件、重命名文件或目录
fs.rename(oldPath,newPath,callback)
fs.renameSync(oldPath,newPath)
fs.rename('temp.txt', 'test.txt', (err)=>{
if(err){
console.log('err:' + err);
} else {
console.log('修改文件名成功');
}
});
- 删除文件
fs.unlink(path,callback(err))
fs.unlinkSync(path)
fs.unlink('test.txt', (err)=>{
if(err){
console.log('err:' + err);
} else {
console.log('删除文件名成功');
}
});
- 目录操作
- 创建一个目录
fs.mkdir(path[,model],callback)
fs.mkdirSync(path[,model])
- 创建一个目录
fs.mkdir(path.join(__dirname, 'test.txt'), (err)=>{
if(err){
console.log(err);
} else {
console.log('创建文件成功');
}
});
- 删除一个空目录
fs.rmdir(path,callback)
fs.rmdirSync(path)
fs.rmdir(path.join(__dirname, 'test.txt'), (err)=>{
if(err){
console.log('err:' + err);
}else{
console.log('删除成功');
}
});
- 读取一个目录
fs.readdir(path,callback(err,files))
fs.readdirSync(path) // => 返回files
fs.readdir(__dirname, (err, files)=>{
if(err){
console.log('err:' + err);
}else{
console.log(files);
}
});
三、缓冲区
- 什么是缓冲区
- 缓冲区就是内存中操作数据的容器
- 只是数据容器而已
- 通过缓冲区可以很方便的操作二进制数据
- 而且在大文件操作时必须有缓冲区
为什么要有缓冲区
JavaScript是比较擅长处理字符串,但是早期的应用场景主要用于处理HTML文档,不会有太大篇幅的数据处理,也不会接触到二进制的数据。而在Node中操作数据、网络通信是没办法完全以字符串的方式操作的,所以在Node中引入了一个二进制的缓冲区的概念:Buffer。缓冲区简单操作
// 创建长度为4个字节的缓冲区
var buffer = new Buffer(4);
// 通过指定编码的方式创建
var buffer = new Buffer('hello', 'utf8');
// 往缓冲区中写数据(假如缓冲区只有4个字节大小)
buffer.write('123456');
console.log(buffer.toString('utf8')); // 输出是1234
node中默认的编码类型都是utf-8;
- 读图片操作
var fs = require('fs');
var path = require('path');
fs.readFile(path.join(__dirname, './test.png'), (error, data)=>{
if(error) console.error(error);
// 将buffer内容读取出来(图片是base64)
console.log(data.toString('base64'));
});
还原生成Base64编码为图片: http://www.atool.org/img2base64.php;
注意: 要在前面添加上data:image/png;base64,
表示图片类型;
- node支持的编码
Buffers 和 JavaScript 字符串对象之间转换时需要一个明确的编码方法。
- 'ascii' - 7位的 ASCII 数据。这种编码方式非常快,它会移除最高位内容
- 'utf8' - 多字节编码 Unicode 字符。大部分网页和文档使用这类编码方式
- 'utf16le' - 2个或4个字节, Little Endian (LE) 编码 Unicode 字符。编码范围 (U+10000 到 U+10FFFF)
- 'ucs2' - 'utf16le'的子集
- 'base64' - Base64 字符编码
- 'binary' - 仅使用每个字符的头8位将原始的二进制信息进行编码
(在需使用 Buffer 的情况下,应该尽量避免使用这个已经过时的编码方式,这个编码方式将会在未来某个版本中弃用)。
- 'hex' - 每个字节都采用 2 进制编码。
在node中是不支持的gbk类型编码的!!!!
对于各种操作系统中编码问题,可以使用第三方模块iconv-lite