大佬教程收集整理的这篇文章主要介绍了Node.js 官网入门教程(四) EventEmitter、HTTP(GET、POST(axios))、Express、fs文件模块(描述符、属性、路径、读取、写入、文件夹),大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。
总结:
通用
API检索网址:API 文档 | Node.js 中文网 (nodejs.cn)
第三方模板引擎:art-template官方文档
npm init 生成 package.json文件。
Express:
koa: 由 Express 背后的同一个团队构建c;旨在变得更简单更轻巧。 新项目的诞生是为了满足创建不兼容的更改而又不破坏现有社区。
readyState
status
- @H_165_7@mIME Type
- @H_165_7@mIME (MultipurposE internet Mail Extensions) 是描述消息内容类型的因特网标准c;说白了也就是文件的媒体类型。浏览器可以根据它来区分文件c;然后决定什么内容用什么形式来显示。
首先c;我们要了解浏览器是如何处理内容的。在浏览器中显示的内容有 HTML、有 XML、有 GIF、还有 Flash ……那么c;浏览器是如何区分它们c;决定什么内容用什么形式来显示呢?答案是 MIME Typec;也就是该资源的媒体类型。
媒体类型通常是通过 http 协议c;由 Web 服务器告知浏览器的c;更准确地说c;是通过 Content-Type 来表示的c;例如:
Content-Type: text/HTML
CLI
- 命令行界面(英语:command-linE interfacec;缩写:CLI)是在图形用户界面得到普及之前使用最为广泛的用户界面c;它通常不支持鼠标c;用户通过键盘输入指令c;计算机接收到指令后c;予以执行。也有人称之为字符用户界面(CUI)。
- 通常认为c;命令行界面(CLI)没有图形用户界面(GUI)那么方便用户操作。因为c;命令行界面的软件通常需要用户记忆操作的命令c;但是c;由于其本身的特点c;命令行界面要较图形用户界面节约计算机系统的资源。在熟记命令的前提下c;使用命令行界面往往要较使用图形用户界面的操作速度要快。所以c;图形用户界面的操作系统中c;都保留着可选的命令行界面。
- GET、POST
- GET请求的数据会附在URL之后(就是把数据放置在http协议头中)c;以?分割URL和传输数据c;参数之间以&相连c;如:login.action?name=hyddd&password=idontknow&verify=%E4%BD%A0%E5%A5%BD。如果数据是英文字母或数字c;则原样发送;如果是空格c;转换为+;如果是中文或其他字符c;则直接把字符串用BASE64加密c;得出如:%E4%BD%A0%E5%A5%BDc;其中5;XX中的XX为该符号以16进制表示的ASCII码值。而与之对应的c;POST把提交的数据放置在http包的包体中c;文章最下面将会有代码示例。
- POST的安全性要比GET的安全性高。注意:这里所说的安全性和上面GET提到的“安全”不是同个概念。上面“安全”的含义仅仅是不作数据修改c;而这里安全的含义是真正的Security的含义。比如:通过GET提交数据c;用户名和密码将明文出现在URL上c;因为:(1)登录页面有可能被浏览器缓存c;(2)其他人查看浏览器的历史纪录c;那么别人就可以拿到你的账号和密码了c;除此之外c;使用GET提交数据还可能会造成Cross-site request forgery攻击(CSRF,跨站请求伪造c;也被称为:one click attack/session riding)。
fs文件模块
如果你在浏览器中使用 JavaScriptc;则你会知道通过事件处理了许多用户的交互:鼠标的单击、键盘按钮的按下、对鼠标移动的反应等等。
在后端c;Node.js 也提供了使用 events
模块构建类似系统的选项。
具体上c;此模块提供了 EventEmitter
类c;用于处理事件。
使用以下代码进行初始化:
const EventEmitter @H_518_262@= require('events')
const eventEmitter @H_518_262@= new EventEmitter()
该对象公开了 on
和 emit
方法。
emit
用于触发事件。on
用于添加回调函数(会在事件被触发时执行)。例如c;创建 start
事件c;并提供一个示例c;通过记录到控制台进行交互:
eventEmitter.on('start', () @H_518_262@=> {
console.log('开始')
})
当运行以下代码时:
eventEmitter.emit('start')
事件处理函数会被触发c;且获得控制台日志。
可以通过将参数作为额外参数传给 emit()
来将参数传给事件处理程序:
eventEmitter.on('start', number @H_518_262@=> {
console.log(`开始 ${number}`)
})
eventEmitter.emit('start', 23)
多个参数:
eventEmitter.on('start', (start, end) @H_518_262@=> {
console.log(`从 ${start} 到 ${end}`)
})
eventEmitter.emit('start', 1, 100)
EventEmitter 对象还公开了其他几个与事件进行交互的方法c;例如:
once()
: 添加单次监听器。removeListener()
/ off()
: 从事件中移除事件监听器。removeAllListeners()
: 移除事件的所有监听器。可以在事件模块的页面 http://nodejs.cn/api/events.html 上阅读其所有详细信息。
这是一个简单的 http web 服务器的示例:
const http @H_518_262@= require('http')
const port @H_518_262@= 3000
const server @H_518_262@= http.createServer((req, res) @H_518_262@=> {
res.statusCode @H_518_262@= 200
res.setHeader('Content-Type', 'text/plain')
res.end('你好世界n')
})
server.listen(port, () @H_518_262@=> {
console.log(`服务器运行在 http://${hostname}:${port}/`)
})
简要分析一下。 这里引入了 http
模块。
使用该模块来创建 http 服务器。
服务器被设置为在指定的 3000
端口上进行监听。 当服务器就绪时c;则 listen
回调函数会被调用。
传入的回调函数会在每次接收到请求时被执行。 每当接收到新的请求时c;request
事件会被调用c;并提供两个对象:一个请求(http.Incomingmessage
对象)和一个响应(http.ServerResponse
对象)。
request
提供了请求的详细信息。 通过它可以访问请求头和请求的数据。
response
用于构造要返回给客户端的数据。
在此示例中:
res.statusCode @H_518_262@= 200
设置 statusCode 属性为 200c;以表明响应成功。
还设置了 Content-Type 响应头:
res.setHeader('Content-Type', 'text/plain')
最后结束并关闭响应c;将内容作为参数添加到 end()
:
res.end('你好世界n')
const https @H_518_262@= require('https')
const options @H_518_262@= {
hostname@H_518_262@: 'nodejs.cn',
port@H_518_262@: 443,
path@H_518_262@: '/todos',
method@H_518_262@: 'GET'
}
const req @H_518_262@= https.request(options, res @H_518_262@=> {
console.log(`状态码: ${res.statusCode}`)
res.on('data', d @H_518_262@=> {
process.stdout.write(d)
})
})
req.on('error', error @H_518_262@=> {
console.error(error)
})
req.end()
const https @H_518_262@= require('https')
const data @H_518_262@= JSON.Stringify({
todo@H_518_262@: '做点事情'
})
const options @H_518_262@= {
hostname@H_518_262@: 'nodejs.cn',
port@H_518_262@: 443,
path@H_518_262@: '/todos',
method@H_518_262@: 'poST',
headers@H_518_262@: {
'Content-Type'@H_518_262@: 'application/json',
'Content-Length'@H_518_262@: data.length
}
}
const req @H_518_262@= https.request(options, res @H_518_262@=> {
console.log(`状态码: ${res.statusCode}`)
res.on('data', d @H_518_262@=> {
process.stdout.write(d)
})
})
req.on('error', error @H_518_262@=> {
console.error(error)
})
req.write(data)
req.end()
PUT 和 deletE 请求使用相同的 POST 请求格式c;只需更改 options.method
的值即可。
在 Node.js 中c;有多种方式可以执行 http POST 请求c;具体取决于要使用的抽象级别。
使用 Node.js 执行 http 请求的最简单的方式是使用 Axios 库:
const axios @H_518_262@= require('axios')
axios
.post('http://nodejs.cn/todos', {
todo@H_518_262@: '做点事情'
})
.then(res @H_518_262@=> {
console.log(`状态码: ${res.statusCode}`)
console.log(res)
})
.catch(error @H_518_262@=> {
console.error(error)
})
Axios 需要使用第三方的库。
也可以只使用 Node.js 的标准模块来发送 POST 请求c;尽管它比前面的选择冗长些:
const https @H_518_262@= require('https')
const data @H_518_262@= JSON.Stringify({
todo@H_518_262@: '做点事情'
})
const options @H_518_262@= {
hostname@H_518_262@: 'nodejs.cn',
port@H_518_262@: 443,
path@H_518_262@: '/todos',
method@H_518_262@: 'poST',
headers@H_518_262@: {
'Content-Type'@H_518_262@: 'application/json',
'Content-Length'@H_518_262@: data.length
}
}
const req @H_518_262@= https.request(options, res @H_518_262@=> {
console.log(`状态码: ${res.statusCode}`)
res.on('data', d @H_518_262@=> {
process.stdout.write(d)
})
})
req.on('error', error @H_518_262@=> {
console.error(error)
})
req.write(data)
req.end()
这是在请求正文中提取以 JSON 格式发送的数据的方式。
如果使用的是 Expressc;则非常简单:使用 body-parser
Node.js 模块。
例如c;获取此请求的正文:
const axios @H_518_262@= require('axios')
axios.post('http://nodejs.cn/todos', {
todo@H_518_262@: '做点事情'
})
这是对应的服务器端代码:
const express @H_518_262@= require('express')
const app @H_518_262@= express()
app.use(
express.urlencoded({
extended@H_518_262@: true
})
)
app.use(express.json())
app.post('/todos', (req, res) @H_518_262@=> {
console.log(req.body.todo)
})
如果不使用 Express 并想在普通的 Node.js 中执行此操作c;则需要做多一点的工作c;因为 Express 抽象了很多工作。
要理解的关键是c;当使用 http.createServer()
初始化 http 服务器时c;服务器会在获得所有 http 请求头(而不是请求正文时)时调用回调。
在连接回调中传入的 request
对象是一个流。
因此c;必须监听要处理的主体内容c;并且其是按数据块处理的。
首先c;通过监听流的 data
事件来获取数据c;然后在数据结束时调用一次流的 end
事件:
const server @H_518_262@= http.createServer((req, res) @H_518_262@=> {
// 可以访问 http 请求头
req.on('data', chunk @H_518_262@=> {
console.log(`可用的数据块: ${chunk}`)
})
req.on('end', () @H_518_262@=> {
//数据结束
})
})
因此c;若要访问数据(假设期望接收到字符串)c;则必须将其放入数组中:
const server @H_518_262@= http.createServer((req, res) @H_518_262@=> {
let data @H_518_262@= '';
req.on('data', chunk @H_518_262@=> {
data @H_518_262@+= chunk;
})
req.on('end', () @H_518_262@=> {
JSON.parse(data).todo // '做点事情'
})
})
在与位于文件系统中的文件进行交互之前c;需要先获取文件的描述符。
文件描述符是使用 fs
模块提供的 open()
方法打开文件后返回的:
const fs @H_518_262@= require('fs')
fs.open('/Users/joe/test.txt', 'R', (err, fd) @H_518_262@=> {
//fd 是文件描述符。
})
注意c;将 r
作为 fs.open()
调用的第二个参数。
该标志意味着打开文件用于读取。
其他常用的标志有:
r+
打开文件用于读写。w+
打开文件用于读写c;将流定位到文件的开头。如果文件不存在则创建文件。a
打开文件用于写入c;将流定位到文件的末尾。如果文件不存在则创建文件。a+
打开文件用于读写c;将流定位到文件的末尾。如果文件不存在则创建文件。也可以使用 fs.openSync
方法打开文件c;该方法会返回文件描述符(而不是在回调中提供):
const fs @H_518_262@= require('fs')
try {
const fd @H_518_262@= fs.openSync('/Users/joe/test.txt', 'R')
} catch (err) {
console.error(err)
}
一旦获得文件描述符c;就可以以任何方式执行所有需要它的操作c;例如调用 fs.open()
以及许多与文件系统交互的其他操作。
每个文件都带有一组详细信息c;可以使用 Node.js 进行检查。
具体地说c;使用 fs
模块提供的 stat()
方法。
调用时传入文件的路径c;一旦 Node.js 获得文件的详细信息c;则会调用传入的回调函数c;并带上两个参数:错误消息和文件属性:
const fs @H_518_262@= require('fs')
fs.stat('/Users/joe/test.txt', (err, stats) @H_518_262@=> {
if (err) {
console.error(err)
return
}
//可以访问 `stats` 中的文件属性
})
Node.js 也提供了同步的方法c;该方法会阻塞线程c;直到文件属性准备就绪为止:
const fs @H_518_262@= require('fs')
try {
const stats @H_518_262@= fs.statSync('/Users/joe/test.txt')
} catch (err) {
console.error(err)
}
文件的信息包含在属性变量中。 可以通过属性提取哪些信息?
很多c;包括:
stats.isFile()
和 stats.isDirectory()
判断文件是否目录或文件。stats.isSymbolicLink()
判断文件是否符号链接。stats.size
获取文件的大小(以字节为单位)。还有其他一些高级的方法c;但是在日常编程中会使用的大部分是这些。
const fs @H_518_262@= require('fs')
fs.stat('/Users/joe/test.txt', (err, stats) @H_518_262@=> {
if (err) {
console.error(err)
return
}
stats.isFile() //true
stats.isDirectory() //false
stats.isSymbolicLink() //false
stats.size //1024000 //= 1MB
})
系统中的每个文件都有路径。
在 Linux 和 macOS 上c;路径可能类似于:
/users/joe/file.txt
C:usersjoefile.txt
当在应用程序中使用路径时需要注意c;因为必须考虑到这种差异。
可以使用以下方式将此模块引入到文件中:
const path @H_518_262@= require('path')
现在可以开始使用其方法。
给定一个路径c;可以使用以下方法从其中提取信息:
dirname
: 获取文件的父文件夹。bas@R_197_8371@
: 获取文件名部分。extname
: 获取文件的扩展名。例如:
const notes @H_518_262@= '/users/joe/notes.txt'
path.dirname(notes) // /users/joe
path.bas@R_197_8371@(notes) // notes.txt
path.extname(notes) // .txt
可以通过为 bas@R_197_8371@
指定第二个参数来获取不带扩展名的文件名:
path.bas@R_197_8371@(notes, path.extname(notes)) //notes
可以使用 path.join()
连接路径的两个或多个片段:
const name @H_518_262@= 'joe'
path.join('/', 'users', name, 'notes.txt') //'/users/joe/notes.txt'
可以使用 path.resolve()
获得相对路径的绝对路径计算:
path.resolve('joe.txt') //'/Users/joe/joe.txt' 如果从主文件夹运行。
在此示例中c;Node.js 只是简单地将 /joe.txt
附加到当前工作目录。 如果指定第二个文件夹参数c;则 resolve
会使用第一个作为第二个的基础:
path.resolve('tmp', 'joe.txt') //'/Users/joe/tmp/joe.txt' 如果从主文件夹运行。
如果第一个参数以斜杠开头c;则表示它是绝对路径
path.resolve('/etc', 'joe.txt') //'/etc/joe.txt'
path.normalize()
是另一个有用的函数c;当包含诸如 .
、..
或双斜杠之类的相对说明符时c;其会尝试计算实际的路径:
path.normalize('/users/joe/..//test.txt') //'/users/test.txt'
解析和规范化都不会检查路径是否存在。 其只是根据获得的信息来计算路径。
在 Node.js 中读取文件最简单的方式是使用 fs.readFile()
方法c;向其传入文件路径、编码、以及会带上文件数据(以及错误)进行调用的回调函数:
const fs @H_518_262@= require('fs')
fs.readFile('/Users/joe/test.txt', 'utf8' , (err, data) @H_518_262@=> {
if (err) {
console.error(err)
return
}
console.log(data)
})
另外c;也可以使用同步的版本 fs.readFileSync()
:
const fs @H_518_262@= require('fs')
try {
const data @H_518_262@= fs.readFileSync('/Users/joe/test.txt', 'utf8')
console.log(data)
} catch (err) {
console.error(err)
}
fs.readFile()
和 fs.readFileSync()
都会在返回数据之前将文件的全部内容读取到内存中。
这意味着大文件会对内存的消耗和程序执行的速度产生重大的影响。
在 Node.js 中写入文件最简单的方式是使用 fs.writeFile()
API。
例如:
const fs @H_518_262@= require('fs')
const content @H_518_262@= '一些内容'
fs.writeFile('/Users/joe/test.txt', content, err @H_518_262@=> {
if (err) {
console.error(err)
return
}
//文件写入成功。
})
另外c;也可以使用同步的版本 fs.writeFileSync()
:
const fs @H_518_262@= require('fs')
const content @H_518_262@= '一些内容'
try {
const data @H_518_262@= fs.writeFileSync('/Users/joe/test.txt', content)
//文件写入成功。
} catch (err) {
console.error(err)
}
默认情况下c;此 API 会替换文件的内容(如果文件已经存在)。
可以通过指定标志来修改默认的行为:
fs.writeFile('/Users/joe/test.txt', content, { flag@H_518_262@: 'a+' }, err @H_518_262@=> {})
可能会使用的标志有:
r+
打开文件用于读写。w+
打开文件用于读写c;将流定位到文件的开头。如果文件不存在则创建文件。a
打开文件用于写入c;将流定位到文件的末尾。如果文件不存在则创建文件。a+
打开文件用于读写c;将流定位到文件的末尾。如果文件不存在则创建文件。(可以在 http://nodejs.cn/api/fs.html#fs_file_system_flags 中查看更多标志)
将内容追加到文件末尾的便捷方法是 fs.appendFile()
(及其对应的 fs.appendFileSync()
):
const content @H_518_262@= '一些内容'
fs.appendFile('file.log', content, err @H_518_262@=> {
if (err) {
console.error(err)
return
}
//完成!
})
所有这些方法都是在将全部内容写入文件之后才会将控制权返回给程序(在异步的版本中c;这意味着执行回调)。
Node.js 的 fs
核心模块提供了许多便捷的方法用于处理文件夹。
使用 fs.access()
检查文件夹是否存在以及 Node.js 是否具有访问权限。
使用 fs.mkdir()
或 fs.mkdirSync()
可以创建新的文件夹。
const fs @H_518_262@= require('fs')
const folderName @H_518_262@= '/Users/joe/test'
try {
if (@H_518_262@!fs.existsSync(folderName)) {
fs.@H_214_242@mkdirSync(folderName)
}
} catch (err) {
console.error(err)
}
使用 fs.readdir()
或 fs.readdirSync()
可以读取目录的内容。
这段代码会读取文件夹的内容(全部的文件和子文件夹)c;并返回它们的相对路径:
const fs @H_518_262@= require('fs')
const path @H_518_262@= require('path')
const folderPath @H_518_262@= '/Users/joe'
fs.readdirSync(folderPath)
可以获取完整的路径:
fs.readdirSync(folderPath).@H_214_242@map(fil@R_197_8371@ @H_518_262@=> {
return path.join(folderPath, fil@R_197_8371@)
})
也可以过滤结果以仅返回文件(排除文件夹):
const isFile @H_518_262@= fil@R_197_8371@ @H_518_262@=> {
return fs.lstatSync(fil@R_197_8371@).isFile()
}
fs.readdirSync(folderPath).@H_214_242@map(fil@R_197_8371@ @H_518_262@=> {
return path.join(folderPath, fil@R_197_8371@)
})
.filter(isFile)
使用 fs.r@R_197_8371@()
或 fs.r@R_197_8371@Sync()
可以重命名文件夹。 第一个参数是当前的路径c;第二个参数是新的路径:
const fs @H_518_262@= require('fs')
fs.r@R_197_8371@('/Users/joe', '/Users/roger', err @H_518_262@=> {
if (err) {
console.error(err)
return
}
//完成
})
fs.r@R_197_8371@Sync()
是同步的版本:
const fs @H_518_262@= require('fs')
try {
fs.r@R_197_8371@Sync('/Users/joe', '/Users/roger')
} catch (err) {
console.error(err)
}
使用 fs.rmdir()
或 fs.rmdirSync()
可以删除文件夹。
删除包含内容的文件夹可能会更复杂。
在这种情况下c;最好安装 fs-extra
模块c;该模块非常受欢迎且维护良好。 它是 fs
模块的直接替代品c;在其之上提供了更多的功能。
在此示例中c;需要的是 remove()
方法。
使用以下命令安装:
npm install fs-extra
并像这样使用它:
const fs @H_518_262@= require('fs-extra')
const folder @H_518_262@= '/Users/joe'
fs.remove(folder, err @H_518_262@=> {
console.error(err)
})
也可以与 promise 一起使用:
fs.remove(folder)
.then(() @H_518_262@=> {
//完成
})
.catch(err @H_518_262@=> {
console.error(err)
})
或使用 async/await:
async function removeFolder(folder) {
try {
await fs.remove(folder)
//完成
} catch (err) {
console.error(err)
}
}
const folder @H_518_262@= '/Users/joe'
removeFolder(folder)
以上是大佬教程为你收集整理的Node.js 官网入门教程(四) EventEmitter、HTTP(GET、POST(axios))、Express、fs文件模块(描述符、属性、路径、读取、写入、文件夹)全部内容,希望文章能够帮你解决Node.js 官网入门教程(四) EventEmitter、HTTP(GET、POST(axios))、Express、fs文件模块(描述符、属性、路径、读取、写入、文件夹)所遇到的程序开发问题。
如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。