node.js安装配置:
下载地址:https://nodejs.org/en/download/
安装教程:https://www.runoob.com/nodejs/nodejs-install-setup.html
在vsCode上下载包工具:Node Debug 和 node-snippets
一、创建简单的服务器
步骤:
1、引入http 协议模块 require('http')
2、创建服务器 createServer()
3、监听服务器端口 listen()
4、服务器运行 node ***.js
const http = require('http');
let port = 8000;
let hostname = "localhost"
const server = http.createServer((req, res) => {
//res.writeHead(200, { "content-Type": "text/html;charset=utf8" });
//设置请求头也可以分开写
//设置状态码
res.statusCode = 200;
res.setHeader("content-Type", "text/html;charset=utf8");
res.write("首页", (err) => {
if (err)
throw err;
});
res.end();
});
server.listen(port, () => {
console.log(`服务器运行在:http://${hostname}:${port}/`);
})
注释:
(1)http 模块的 createServer()
方法 的callback
中有两个参数,分别为 req
和 res
。分别代表 request 请求头 ; response 响应头。
(2)上例中的res.end()
:向服务器发出信号,表明已发送所有响应头和主体,该服务器应该视为此消息已完成。 必须在每个响应上调用此 response.end()
方法。
res.end()
参数为data
,encoding
,callback
。
如果指定了 data
,则相当于调用 response.write(data, encoding)
之后再调用 response.end(callback)
。
如果指定了 callback
,则当响应流完成时将调用它。
(2)req 有一个属性是 req.url
,用于返回请求的地址。
注意:
在node.js中文件修改需要重启服务:先 Ctrl+C 结束当前进程,再 node ***.js
重新运行服务器。
这样开发比较慢,可以安装自启动工具 supervisor。
cnpm install --save-dev supervisor
安装完成之后启动文件使用supervisor ***.js
启动服务之后修改代码会自动启动服务 。
二、http模块的 get() 和 request() 方法
由于大多数请求都是不带请求体的 GET 请求,因此 Node.js 提供了这两个便捷的方法。
1、http.get(url,(res)=>{})
url 参数是请求的路径,res 里边是响应信息。
这个方法与 http.request()
唯一的区别是,它将请求方法设置为 GET
并且会自动调用 req.end()
。
//http模块的get方法
let http = require('http');
http.get("http://nodejs.cn/search", (res) => {
console.log(res.statusCode);
});
2、http.request(url,(res)=>{})
url 参数是请求的路径,res里边是响应信息。
使用 http.request()
时,必须始终调用 req.end()
来表示请求的结束,即使没有数据被写入请求主体。
三、url 路径解析模块
url 路径解析模块 是 node.js 的内置模块,使用时可以直接引入 :
const url=require('url');
引入后就可以使用了:
使用时要注意: req
有一个自带的默认路径 "/favicon.ico"
,使用时需要处理。否则 req.url
就会输出两个路径,一个是该默认路径,一个是实际请求路径。
//处理"/favicon.ico"
if (req.url != "/favicon.ico") {
//代码
}
路径解析模块里边的方法:
url.parse(req.url, true)
:路径解析,true
表示将参数解析成 json 格式,默认是false。
解析后输出:
上边输出结果中:
query
是 get
请求传的值(post
传值使用req.body
);
pathname
是路径名;
path
是路径。
四、commonjs ——模块化系统
Commonjs是模块化的标准,node.js 就是commonjs 模块化的实现。
在nodejs中,http,url,fs都是 nodejs 的内置模块,可以直接引入后使用。
Commonjs中自定义模块的实现:
在nodejs中将公共的功能抽离为单独的 js 文件作为模块,在外部是没有办法访问的(类似后端的私有属性和方法);
要想使用模块,就必须在模块里边通过exports
或者module.exports
暴露属性或者方法。在需要的模块使用require
引入模块。(类似于自己定义依赖,就是自己做一个文件,把它放到node_modules依赖包文件里边)
使用require
引入自定义模块时:
(1)可以直接使用文件相对目录引入,也可以使用以下方法:
(2)Nodejs 可以自动找 node_modules 文件下的文件,如果 node_modules 文件夹下有文件夹,可以cd
进入这个文件,使用cnpm init –yes
安装当前文件的 package.json
配置文件,就可以直接require(‘filename’)
请求了;
具体步骤:
新建
node_modules
文件夹;
将自定义模块放到一个同名文件夹中,再将该文件夹放到node_modules
里边;
cd 进入自定义模块所在文件夹;
使用cnpm init –yes
安装当前文件的package.json
配置文件;
就可以直接require(‘filename’)
请求了。
五、 第三方模块安装和 package.json 项目文件
1、package.json 项目文件
创建node项目之后,先给整个项目安装 package.json 项目文件:在项目目录下 cnpm init --yes
。
安装后就类似于 vue 里边的 package.json。
--yes
强制按照严格模式命名。
2、第三方模块的安装和使用
安装之后直接 require
即可。
比如,引入md5-node
模块:
安装:
cnpm install --save-dev md5-node
引入:
let md5=require(“md5-node”)
引入之后就可以使用了。
md5('1234')
六、FS 文件系统模块
FS
——文件系统模块,是一组文件操作api
,用来进行文件的读取和写入。
Fs模块中的方法均有同步(阻塞式)和异步(非阻塞式)版本。建议使用异步,性能更高,速度更快,没有阻塞。
异步方法函数最后一个参数为回调函数,回调函数的第一个参数包含了错误信息(error
)。
使用:因为是内置模块,所以直接 require 请求即可。
常用api:
(1)fs.readFile 异步读取文件
参数:path
,
flags
(一般写r+,可读可写),
mode
(设置文件模式(权限),默认为0666可读可写),
callback(err,str)
第一个参数err是错误信息,第二个参数str是读取到的数据(buffer类型,打出到页面要toString)。
fs.readFile("./duction/stu.txt", { flag: "r+", mode: 0666 }, (err, str) => {
if (err)
throw err;
//读取到的是buffer值
console.log(str);
res.write(str.toString());
res.end()
})
(2)Fs.readFileSync同步读取文件
参数是path
let str=fs.readFileSync("./duction/stu.txt")
res.write(str.toString());
(3)Fs.open 异步打开文件
参数:path
,callback(err,fd)
fd
是文件描述符 。
fs.open("./duction/stu.txt", (err, fd) => {
if (err) {
console.log("文件打开失败");
throw err;
}
else console.log("文件打开", fd);
})
(4)fs.stat 异步检测文件信息
参数:path
,callback(err,stats)
。
stats
的方法:
stats.isFile()
检测是否是文件;
stats.isDirectory()
检测是否是文件夹。
fs.stat("./duction/stu.txt", (err, stat) => {
if (err)
throw err;
else {
console.log(stat.isFile());
console.log(stat.isDirectory());
}
})
面试题:
有一个文件夹,包含子文件和子文件夹,检测哪个是文件,哪个是文件夹?
let baseurl = "./";
let basename = "duction";
let check = (base, name) => {
let src = base + name;
fs.stat(src, (err, stats) => {
if (err)
throw err;
else {
if (stats.isDirectory()) {
//目录
console.log(name, "---directory");
//如果是目录 打开目录
fs.open(src, (error, fd) => {
if (error)
throw error;
else {
//读取目录
fs.readdir(src, (e, files) => {
if (e)
throw e;
else {
//对读取到的每个文件进行检测
for (let val of files) {
let url = src + "/";
check(url, val);
}
}
})
}
})
}
else {
//是文件
console.log(name, "---file");
}
}
})
}
check(baseurl, basename);
(5)fs.writeFile 异步写入文件
写入时没有该文件,则自动创建。
是一次性写入,会覆盖原先的文本。
参数:file
(文件名或文件描述符),
data
(要写入文件的数据,可以是String字符串或Buffer缓冲对象),
options
(对象),
callback(err)
fs.writeFile("./duction/data.txt", "这是写入的内容", { flag: "w", mode: 0666 }, (err) => {
if (err) {
console.log("写入失败");
throw err;
}
else {
console.log("写入成功");
}
})
(6)fs.read 异步读取文件(可以分段读取)
读取前要先打开文件。
参数:fd
(通过fs.open()
方法返回的文件描述符),
buffer
(数据写入的缓冲区),
offset
(缓冲区写入的写入偏移量),
length
(要从文件中读取的字节数),
position
(文件读取的起始位置,如果position的值为null,从当前文件的指针位置读取),
callback(err,bytesRead,buffer)
let buffer = Buffer.alloc(500);
fs.open("./duction/stu.txt", (err, fd) => {
if (err)
throw err;
else {
console.log("文件打开,开始读取!");
fs.read(fd, buffer, 0, buffer.length, null, (error, bytes) => {
if (error)
throw error;
else {
//读取完成后关闭文件 操作读取到的buffer
fs.close(fd, (e) => {
if (e)
throw e;
else {
console.log(buffer.toString(),bytes);
}
})
}
})
}
})
注意:
使用分段读取时,可能会把读取到的最后一个字截成两半,造成乱码。如下图:
解决方案:
将每次读取到的buffer
存到一个完整的buffer
里边,最后一次性转成字符串(buffer本身是一个存储二进制字节的数组)
代码如下:
let buff = Buffer.alloc(0);
let buffer = Buffer.alloc(500);
fs.open("./duction/stu.txt", "r+", (err, fd) => {
if (err)
throw err;
else {
let count = 0;
console.log("文件打开,开始读取!");
let readfile = () => {
//每次读取的位置 buffer.length*count
fs.read(fd, buffer, 0, buffer.length, buffer.length * count, (error, bytes) => {
if (error)
throw error;
else {
if (bytes > 0) {
buff = Buffer.concat([buff, buffer.slice(0, bytes)]);
count++;
readfile();
}
else {
//读取完成后关闭文件 操作读取到的buffer
fs.close(fd, (e) => {
if (e)
throw e;
else {
console.log("文件关闭成功!");
console.log(buff.toString());
}
})
}
}
})
};
readfile();
}
})
(7)fs.close 异步关闭文件
参数:fd
,callback(err)
(8)fs.ftruncate 异步截取文件
功能: 截取一定字节内的文件内容,超出部分将被去除。
使用前得先打开文件
参数:fd
,len
(文件内容截取的长度),callback(err)
let buffer = Buffer.alloc(2048);
fs.open("./duction/stu.txt", "r+", (err, fd) => {
if (err)
throw err;
else {
console.log("文件打开成功!");
//开始截取
fs.ftruncate(fd, 300, (error) => {
if (error) {
throw error;
console.log("文件截取失败!");
}
else {
console.log("文件截取成功!");
//读取文件
fs.read(fd, buffer, 0, buffer.length, null, (e, bytes) => {
if (e)
throw e;
console.log(buffer.toString());
})
}
})
}
})
上边代码截取了300个字符的内容,再读取这个文件时,就只能读取到300个字符的内容。
(9)fs.unlink 删除文件
(创建文件: fs.writeFile
会自动创建)
参数:path
,callback(err)
(10)fs.mkdir 创建目录
参数:path
;
options
包含recursive -是否以递归的方式创建目录,默认为false;mode-设置目录权限,默认为0777 ;
callback(err)
。
案例:
登录QQ,创建对应的目录,如果存在,删除目录;如果不存在,创建目录并在该目录下写入文件。
let query = url.parse(req.url, true).query;
let qq = query.qq;
let url = "./" + qq;
fs.mkdir(url, (err) => {
if (err) {
console.log("该目录已经存在!");
//如果目录已存在 删除目录
fs.rmdir(url, (error) => {
if (error) {
//目录里边有内容时 会删除失败
console.log("目录删除失败!");
//删除目录里边的文件
fs.unlink(url + "/data.txt", (errinfo) => {
if (errinfo) {
console.log("文件删除失败!");
}
else {
console.log("文件删除成功!");
}
})
}
else {
console.log("删除成功!");
}
})
}
else {
console.log("目录创建成功!准备写入文件!");
//在该目录下写入文件
fs.writeFile(url + "/data.txt", "写入的数据", (e) => {
if (e) {
console.log("写入文件失败!");
}
else {
console.log("写入文件成功!");
}
})
}
})
(11)fs.readdir 读取目录
参数:path
,callback(err,files)
files 是目录中文件的名称的数组
(12)fs.rmdir 删除目录
该目录里边有文件时不能直接删除
参数:path
,callback(err)
(13)fs.rename 重命名
参数:oldpath
,newpath
,callback(err)
fs.rename("./duction", "./direction", (err) => {
if (err) {
console.log("重命名失败");
}
else {
console.log("重命名成功");
}
})
(14)fs.appendFile 文件追加式写入
异步的追加数据到文件,如果文件尚不存在,就创建文件。
参数:path
,data
,options
,callback(err)
fs.appendFile("./direction/data.txt", "\n这是追加的内容", (err) => {
if (err) {
console.log("追加失败!");
}
else {
console.log("追加成功!");
}
})