包与npm
nodejs中除了它自己提供的核心模块之外,我们还可以自定义模块,也可以使用第三方模块。nodejs中的第三方模块是由包组成,可以通过包来对一组具有相互依赖关系的模块进行统一管理。
从上面这个图可以看出,一个包是由多个模块组成的,模块之间具有相互依赖的关系。
完成符合CommonJS规范的包目录一般包含如下这些文件:
- package.json:包描述文件
- bin:存放可执行二进制文件的目录
- lib:存放javascript代码的目录
- doc :存放文档的目录
第三方模块
package.json
- dependencies:配置当前程序所依赖的其他包
- devDependencies:配置当前程序所依赖的其他包(主要是指工具类的配置)
上面版本数字之前的符号释义:
^
:表示第一位版本号不变,后面两位取最新~
:表示前两位不变,最后一位取最新*
:表示全部取最新
fs模块
/**
* 1.fs.stat 检测是文件还是目录
* 2.fs.mkdir 创建目录
* 3.fs.writeFile 创建写入文件
* 4.fs.appendFile 追加文件
* 5.fs.readFile 读取文件
* 6.fs.readdir 读取目录
* 7.fs.rename 重命名
* 8.fs.rmdir 删除目录
* 9.fs.unlink 删除文件
*/
const fs = require('fs');
// 1 fs.stat 检测是文件还是目录
fs.stat('./../demo01_http', (err, data) => {
if (err) {
console.log(err);
return;
}
console.log(`是文件:${data.isFile()}`);
console.log(`是目录:${data.isDirectory()}`);
})
// 2.fs.mkdir 创建目录
/**
* 参数说明:
* 1 path:将创建的目录路径
* 2 mode:目录权限(读写权限,如777),可不写
* 3 callback:回调,传递异常参数err
*/
fs.mkdir('./css', (err) => {
if (err) {
console.log(err);
return;
}
console.log("创建成功");
})
// 3.fs.writeFile 创建写入文件
/**
* 参数说明:
* 1 path:将创建的文件路径
* 2 data:在创建好的文件中写入的内容
* 3 options: 可省略
* - encoding:string,可选值,默认utf-8
* - mode:number,文件的读写权限,默认438
* - flag:string,默认值‘w’
* 4 callback:回调,传递异常参数err
*/
// ⚠️:如果创建写入的文件已存在,那么后面创建的这个文件的写入内容会覆盖之前的文件内容
fs.writeFile('./html/a.html', 'hello world', (err) => {
if (err) {
console.log(err);
return;
}
console.log("创建写入文件成功");
})
// 4.fs.appendFile 追加文件
// ⚠️:如果文件存在的话,会在该文件内容后面追加内容
fs.appendFile('./css/base.css', 'body{background: pink}', (err) => {
if (err) {
console.log(err);
return;
}
console.log("appendFile成功");
})
// 5.fs.readFile 读取文件
fs.readFile('./html/a.html', (err, data) => {
if (err) {
console.log(err); // 路径错误时:[Error: ENOENT: no such file or directory, open './aaa/a.html']
return;
}
console.log(data); // <Buffer 68 65 6c 6c 6f 20 77 6f 72 6c 64>
// 将buffer转换成string类型
console.log(data.toString()); // hello world
})
// 6.fs.readdir 读取目录
fs.readdir('../demo05_fs', (err, data) => {
if (err) {
console.log(err);
return;
}
console.log(data); // [ 'app.js', 'css', 'html', 'package.json' ]
})
// 7.fs.rename 重命名
/**
* 功能:
* 1: 表示重命名
* 2: 表示移动文件
*/
// 1
fs.rename('./css/aaa.css', './css/index.css', (err) => {
if (err) {
console.log(err);
return;
}
console.log('重命名成功');
})
// 2
fs.rename('./css/index.css', './html/index.css', (err) => {
if (err) {
console.log(err);
return;
}
console.log('移动文件成功');
})
// 8.fs.rmdir 删除目录
// ⚠️:如果目录中有文件的话是删除不了这个目录的,需要先通过fs.unlink()删除了文件之后再删除目录
fs.rmdir('./aaa', (err) => {
if (err) {
console.log(err);
return;
}
console.log('删除目录成功');
})
// 9.fs.unlink 删除文件
fs.unlink('./aaa/index.html', (err) => {
if (err) {
console.log(err);
return;
}
console.log('删除文件成功');
})
fs demo
- 判断服务器上有没有upload目录,没有的话创建这个目录,有的话不做操作
// 判断服务器上有没有upload目录,没有的话创建这个目录,有的话不做操作
const fs = require('fs');
var path = './upload';
fs.stat(path, (err, data) => {
if (err) {
// 如果目录或文件不存在,执行创建目录
mkdir(path);
return;
}
if (!data.isDirectory()) {
// 如果是个文件,需要先删除这个文件,再执行创建目录
fs.unlink(path, (err) => {
if (!err) {
mkdir(path);
} else {
console.log("请检测传入的数据是否正确");
}
})
}
})
// 创建目录的方法
function mkdir(dir) {
fs.mkdir(dir, (err) => {
if (err) {
console.log(err);
return;
}
})
}
要创建多级目录还可以在npm里面搜索mkdirp
这个模块,它可以创建多级目录
- wwwroot文件夹下面有images,css,js以及index.html,找出wwwroot目录下面的所有目录
⚠️用下面的这种方法来实现是不对的
var fs = require('fs');
var path = './wwwroot';
var dirArr = [];
fs.readdir(path, (err, data) => {
if (err) {
console.log(err);
return;
}
for (var i = 0; i < data.length; i++) {
fs.stat(path + '/' + data[i], (error, stats) => {
if (stats.isDirectory()) {
dirArr.push(data[i]);
}
})
}
console.log(dirArr); //[]
})
console.log(dirArr); //[]
因为这里的这个for循环里面的fs.stat(),存在异步的问题,所以在后面打印dirArr时,两个都是空的
改写:通过匿名自执行函数实现递归
// wwwroot文件夹下面有images,css,js以及index.html,找出wwwroot目录下面的所有目录
var fs = require('fs');
var path = './wwwroot';
var dirArr = [];
fs.readdir(path, (err, data) => {
if (err) {
console.log(err);
return;
}
// 通过匿名自执行函数实现递归
// 首先检测第0个,检测完成后在内部再调用这个方法取检测下一个
(function getDir(i) {
if (i == data.length) { // 执行完成
console.log(dirArr);
return;
}
fs.stat(path + '/' + data[i], (error, stats) => {
if (stats.isDirectory()) {
dirArr.push(data[i]);
}
getDir(i + 1);
})
})(0)
})
获取异步方法里面的数据demo
这里有两种方法来实现
// 外部获取异步方法里面的数据
function getData(callback) {
setTimeout(function () {
var name = 'doris'
callback(name);
}, 1000)
}
// 1 ES6之前可以通过回调函数来获取
getData(function (aaa) {
console.log(aaa);
})
// 2 ES6之后可以用promise来获取
/**
* promise来处理异步,有两个参数:
* resolve:成功的回调函数
* reject:失败的回调函数
*/
var p = new Promise(function (resolve, reject) {
setTimeout(function () {
var name = 'doris 11'
resolve(name);
}, 1000)
})
// 获取里面的数据
p.then(function(data){
console.log(data);
});
也可以对上面的第二种方法(promise)来进行一个简化封装
// 2 ES6之后可以用promise来获取
/**
* promise来处理异步,有两个参数:
* resolve:成功的回调函数
* reject:失败的回调函数
*/
function getData(resolve, reject) {
setTimeout(function () {
var name = 'doris 11'
resolve(name);
}, 1000)
}
var p = new Promise(getData);
// 获取里面的数据
p.then(function(data){
console.log(data);
});
async/await
-
async
用于申明一个异步的function(让方法变成异步)
-
await
用于等待一个异步方法执行完成(
但是必须用在async异步方法里面,否则报错
)
function aaa(){
return "Hihi~";
}
console.log(aaa()); // Hihi~
async function bbb(){
return "Hihihi~";
}
console.log(bbb()); // Promise { 'Hihihi~' }
// async function bbb(){
// return "Hihihi~";
// }
// console.log(await bbb()); // 这样会报错。因为await必须得用在async中
async function bbb(){
return "HihihiHi~";
}
async function main(){
var data = await bbb(); // 获取异步方法里面得数据
console.log(data); // HihihiHi~
}
main();
用async/await方法改写之前的读取目录的demo
const fs = require('fs');
// 1 定义一个 isDir 方法,判断一个资源到底是目录还是文件
async function isDir(path) {
return new Promise((resolve, reject) => {
fs.stat(path, (error, stats) => {
if (error) {
console.log(error);
reject(error);
return;
}
if (stats.isDirectory()) {
resolve(true)
} else {
resolve(false);
}
})
})
}
// 2 获取wwwroot里面的所有资源,循环遍历
var path = './wwwroot';
var dirArr = [];
function main() {
// 注意async加的地方必须得是await方法的外部(离 await 最近的方法前面加 async)
fs.readdir(path, async (err, data) => {
if (err) {
console.log(err);
return;
}
for (var i = 0; i < data.length; i++) {
if (await isDir(path + '/' + data[i])) {
dirArr.push(data[i]);
}
}
console.log(dirArr); // [ 'css', 'images', 'js', 'xxx' ]
})
}
main();
以流的方式来读取/写入文件
比较大的文件时可以用这个方法
- 以流的方式读取
const fs = require('fs');
var readStream = fs.createReadStream('./data/input.txt');
var count = 0;
var str = '';
readStream.on('data', (data) => {
str += data;
count++;
})
readStream.on('end', () => {
console.log(str);
console.log(count); // 2 这里这个2的意思是以流的方式读取了两次
})
readStream.on('error', (err) => {
console.log(err);
})
- 以流的方式写入
const fs = require('fs');
var str = '';
for (var i = 0; i < 500; i++) {
str += 'hihihihihihihihihihihihi~\n';
}
var writeStream = fs.createWriteStream('./data/output.txt');
writeStream.write(str);
// 标记写入完成
writeStream.end();
writeStream.on('finish', ()=>{
console.log('写入完成');
})
- 管道流(复制)
const fs = require('fs');
var readStream = fs.createReadStream('./pic.png');
var writeStream = fs.createWriteStream('./data/pic.png');
readStream.pipe(writeStream);