学习记录:DAY33
前端学习之旅:Node.js 模块与 HTTP 服务
前言----------------------------------------
又是许久许久没有更新,在苦哈哈弄完期末,然后花一天时间把计算机网络课设写了之后。现在又即将回到前后端学习的状态。我想现在正处于一个调整期的状态。一个是随着 blog 的不断堆积,有必要把它们整理成更具有逻辑性的知识片。另一个是我需要了解当前前后端需要学习的路线,这样我可以有一个更明确地学习规划。言不在多,开始工作吧!
日程----------------------------------------
以后不能熬太多夜了,今天又是 9 点多起的床,这样太消耗身体了。今天早上的任务是整理 blog 的内容。又弄了一下午的课设,在我不懈努力之下,我终于是选择了放弃,麻了。
-----------6.23-------------
今天开始学 Nodejs,学了 buffer 和 fs 模块,来写写 blog
一放假早上就起不来了,得慢慢把时差调回来
晚上七点半了,把 http 模块学得差不多了,做做 blog
学习内容----------------------------------------
省流
- 分享一下近期有价值的内容
- Nodejs buffer 模块
- Nodejs fs 模块
- Nodejs http 模块
1. 分享一下近期有价值的内容
- 用于学习 git 的可实践网站:Learn Git Branching 是一个非常实用的网站,可以帮助你通过实践来学习 Git 的各种操作。
- 用于快速搭建虚拟局域网环境:Happynet 是一个基于 N2N 的工具,可以帮助你快速搭建虚拟局域网环境,非常适合需要远程访问和控制设备的场景。
2. Nodejs buffer 模块
用于内存的分配,以下是常见的创建方式:
- 创建:
let buf = Buffer.alloc(10);
- 非安全创建:
let buf_2 = Buffer.allocUnsafe(10);
- 基于内容创建:
let buf_3 = Buffer.from("hello");
- 获取的 buf 是一个数组:
let buf_4 = Buffer.from([85, 234, 231, 21, 85, 94]); console.log(buf_4[0].toString());
3. Nodejs fs 模块
用于文件操作,以下是常见的操作:
- 导入模块:
const fs = require('fs');
- 写入文件:
- 异步写入:
fs.writeFile('./test.txt', 'test12356', err => { if (err == null) { console.log('写入成功'); } });
- 同步写入:
fs.writeFileSync('./test.txt', 'test2441');
- 追加写入:
fs.appendFile('./test.txt', ',not my test', err => {});
- 流式写入:
const ws = fs.createWriteStream('./test.txt', { flags: 'a' }); ws.write("\r\n我"); ws.write('\r\n是'); ws.write('\r\n谁?'); ws.end();
- 异步写入:
- 异步特性:当程序进行到异步代码块时,会创建一个新线程处理异步代码,程序会继续向下执行。线程处理完异步代码后,通过回调函数压入任务队列中。这种异步特性在 Nodejs 非常常见。
- 路径问题:使用
__dirname
可以获取当前的执行文件所在目录,通过path
模块拼接路径:const path = require('path'); const filepath = path.join(__dirname, 'test.txt');
- 额外 API 说明:
API 说明 path.resolve 拼接规范的绝对路径 常用 path.sep 获取操作系统的路径分隔符 path.parse 解析路径并返回对象 path.basename 获取路径的基础名称 path.dirname 获取路径的目录名 path.extname 获取路径的扩展名 - 文件读取:
- 异步读取:
fs.readFile(filepath, (err, data) => { console.log(data.toString()); });
- 流式读取:
const rs = fs.createReadStream(filepath); rs.on('data', chunk => { console.log(chunk.toString()); });
- 读取 + 写入:
ws = fs.createWriteStream(path.join(__dirname, 'test2.txt')); rs.pipe(ws);
- 异步读取:
- 重命名:
fs.rename(path.join(__dirname, 'test2.txt'), path.join(__dirname, 'test3.txt'), err => {});
- 删除:
fs.unlink(path.join(__dirname, 'test3.txt'), err => {});
- 在 14.4 版本后添加了
rm
方法,两者的功能类似:fs.rm(path.join(__dirname, 'test3.txt'), err => {});
- 文件夹操作:
- 创建文件夹:
fs.mkdir(path.join(__dirname, 'dir1'), err => {});
- 递归创建:
fs.mkdir(path.join(__dirname, 'dir2/3/4/5'), { recursive: true }, err => {});
- 读取文件夹:
fs.readdir(__dirname, (err, data) => { console.log(data); });
- 删除文件夹:
fs.rmdir(path.join(__dirname, 'dir1'), err => {});
- 递归删除:
fs.rm(path.join(__dirname, 'dir2'), { recursive: true }, err => {});
- 创建文件夹:
- 获取资源的信息:
获取到的内容:fs.stat(__dirname + '/test.txt', (err, data) => {console.log(data); });
可以通过自定义输出来让内容更加格式化:Stats {dev: 0,mode: 33206,nlink: 1,uid: 0,gid: 0,rdev: 0,blksize: 4096,ino: 7318349395273121,size: 49,blocks: 0,atimeMs: 1750658164362.0237,mtimeMs: 1750652965429.8325,ctimeMs: 1750656700147.115,birthtimeMs: 1750652121205.9016 }
结果如下:fs.stat(__dirname + '/test.txt', (err, stats) => {console.log('文件信息:', {isFile: stats.isFile(), // 是否是文件isDirectory: stats.isDirectory(), // 是否是目录size: stats.size + ' bytes', // 文件大小createdAt: stats.birthtime, // 创建时间(Date 对象)modifiedAt: stats.mtime, // 修改时间(Date 对象)accessedAt: stats.atime, // 访问时间(Date 对象)}); });
文件信息: {isFile: true,isDirectory: false,size: '49 bytes',createdAt: 2025-06-23T04:15:21.206Z,modifiedAt: 2025-06-23T04:29:25.430Z,accessedAt: 2025-06-23T05:56:04.362Z }
4. Nodejs http 模块
通过 Nodejs 来控制 http 相关的请求和响应内容:
- 启动服务:
const http = require('http'); const server = http.createServer((req, rep) => {//设置响应头 utf-8rep.setHeader('content-type', 'text/html;charset=utf-8');//设置响应体rep.end('你好'); }); server.listen(9000, () => {console.log('服务已经启动'); });
- 获取请求报文:
含义 语法 请求方法 request.method
请求版本 request.httpVersion
请求路径 request.url
URL 路径 require('url').parse(request.url).pathname
URL 查询字符串 require('url').parse(request.url, true).query
请求头 request.headers
请求体 request.on('data', function(chunk){})
request.on('end', function(){});
比较常用的是请求方法和请求路径。获取 url 路径:
假设现在访问const server = http.createServer((req, rep) => {...//实例化 URL 对象let url = new URL(req.url, 'http://127.0.0.1');//输出路径console.log(url.pathname);//输出查询字符串console.log(url.searchParams.get('key'));... });
http://localhost:9000/heyi?key=124
,则控制台返回:/heyi 124
- 设置响应报文:
作用 语法 设置响应状态码 response.statusCode
设置响应状态描述 response.statusMessage
设置响应头信息 response.setHeader('头名', '头值')
设置响应体 response.write('xx')
/>
response.end('xxx')
注意 response.end()
代表了响应处理的结束,会将响应结果返回给网页。一般设置内容用response.write()
更加灵活一点,将end
仅作为结尾。响应体是可以设置为一个文件 / 文件流的。通过设置响应体来加载 html 界面:const http = require('http'); const fs = require('fs'); const server = http.createServer((req, rep) => {let html = fs.readFileSync(__dirname + '/test.html');rep.end(html); }); server.listen(9000, () => {});
- 页面加载基本过程:网页的资源不是一次性全部获取的,以下面的为例子:
首先我们手动加载了 html 资源,当浏览器发现 html 引用了 css,js 等资源时,它会向服务器发送对应的资源请求。如果是像我们之前那种写法,无论什么请求,都获取到了 html 资源,是肯定会出错的。我们需要动态的为资源请求进行相应请求体设置:<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><link rel="stylesheet" href="./test.css"> </head> <body><h1>你好 bro</h1><button index="btn1" class="style">点我</button><script src="test.js"></script> </body> </html>
const server = http.createServer((req, rep) => {let { pathname } = new URL(req.url, 'http://127.0.0.1');//拼接文件路径if (req.url == '/') pathname = '/test.html';let filePath = __dirname + pathname;//异步读取资源fs.readFile(filePath, (err, data) => {if (err) {rep.statusCode = 500;rep.end('文件读取失败');return;}rep.end(data);}); });
- 网页 URL 路径:网页中的 URL 主要分为两大类:相对路径与绝对路径。
- 绝对路径:
形式 特点 http://atguigu.com/web
直接向目标资源发送请求,容易理解。网站的外链会用到此形式 //atguigu.com/web
与页面 URL 的协议拼接形成完整 URL 再发送请求。大型网站用的比较多 /web
与页面 URL 的协议、主机名、端口拼接形成完整 URL 再发送请求。中小型网站 - 相对路径:相对路径在发送请求时,需要与当前页面 URL 路径进行计算,得到完整 URL 后,再发送请求,学习阶段用的较多。例如当前网页 url 为
http://www.atguigu.com/course/h5.html
:形式 最终的 URL ./css/app.css
http://www.atguigu.com/course/css/app.css
js/app.js
http://www.atguigu.com/course/js/app.js
../img/logo.png
http://www.atguigu.com/img/logo.png
../../mp4/show.mp4
http://www.atguigu.com/mp4/show.mp4
- 绝对路径:
- 设置资源类型:媒体(MIME)类型是一种标准,用来表示文档、文件或字节流的性质和格式。
MIME 类型结构 例子 type/[subtype] text/html, text/css, image/jpeg, image/png, application/json HTTP 服务可以设置响应头 Content-Type
来表明响应体的 MIME 类型,浏览器会根据该类型决定如何处理响应体。下面是常见文件对应的 MIME 类型:文件类型 MIME类型 ---------- --------------------------- html text/html css text/css js text/javascript png image/png jpg image/jpeg gif image/gif mp4 video/mp4 mp3 audio/mpeg json application/json 基于这个,我们可以自己规范化设置 mine 值:
事实上,即使没有设置 mine,浏览器的嗅探机制也会根据返回的响应内容进行 mine 的设置。而中文字符集的设置,往往只需要设置 html 界面的,其他资源会基于 html 的字符集进行设置(尽管直接在网页读取资源时仍然是乱码):const mines = {html: 'text/html',css: 'text/css',js: 'text/javascript' }; const server = http.createServer((req, rep) => {let { pathname } = new URL(req.url, 'http://127.0.0.1');//拼接文件路径if (req.url == '/') pathname = '/test.html';let filePath = __dirname + pathname;//获取文件后缀名 //.txt -> txtlet ext = path.extname(filePath).slice(1);//获取对应的类型let type = mines[ext];if (type) {rep.setHeader('content-type', type + ';charset=utf-8');} else {rep.end('类型获取失败');}//异步读取资源...... });
rep.setHeader('content-type', type + ';charset=utf-8');