Marco Nie - node.js
type: channel
link: https://blog.niekun.net/category/node/
items: node.js 入门教程之十四 -- TypeScript
items: node.js 入门教程之十三 -- environment，error handler 和 log object
items: node.js 入门教程之十二 -- Streams 流
items: node.js 入门教程之十一 -- buffers 缓存
items: node.js 入门教程之十 -- http 模块
items: node.js 入门教程之九 -- events 模块
items: node.js 入门教程之八 -- os 模块
items: node.js 入门教程之七 -- fs 和 path 模块
items: node.js 入门教程之六 -- FileSystem
items: node.js 入门教程之五 -- HTTP request and response

node.js 入门教程之十四 -- TypeScript
type: item
date: 2021-02-21T14:02:00+00:00
description: TypeScript 是微软开发的开源编程语言，在全世界的开发者中流行。本质上来说 typescript 是 JavaScript 的超集，为其添加了很多功能。标志性的就是添加了 static type 静态类型定义，它可以声明期待的数据类型，例如：声明 function 中的期待的传入数据类型，期望的返回值的类型，object 的结构及 property 的数据类型等。typescript 是一个强大的工具，给 JavaScript 项目开启了新的世界。它使我们的代码更加的安全有力，在程序发布前就可以防止很多的 bug 出现。在编写代码期间就能够及时反映出问题点，并且它已经被整合在现代的编译器中，如 VS code。语法说明下面是一个 typescript 代码示例：type User = { name: string; age: number; }; function isAdult(user: User): boolean { return user.age >= 18; } const justine: User = { name: 'Justine', age: 23, }; const isJustineAnAdult: boolean = isAdult(justine);首先我们通过 type 关键词定义一个自定义的 object 类型 User，其中定义了 User 的两个 properties 及其数据类型。然后在定义的 function isAudlt 中接收一个参数，此参数类型为 User 类型，然后返回值类型为 boolean 类型。然后我们定义了 justine object 且其类型为 User。在其中定义的两个 properties 也符合 User 中的类型。最后定义了变量 isJustineAnAdult 且其数据类型为 boolean，将 isAudlt 的返回值赋给它。如果我们在编程中没有使用指定的数据类型，typescript 将会给出警告提醒我们错误的地方，比如上面的示例中修改 justine 的 age 为一个字符串：在 vs code 中会提示此处有错误。另一方面也并不需要将所有地方的数据类型都做出声明，typescript 会自动推断出需要的类型。例如：即使我们不给 isJustineAnAdult 声明 boolean 类型，typescript 也会自动推断它是 boolean 类型的。那么如何运行 typescript 代码呢？首先需要通过 npm 安装 typescript，它会安装我们需要的一些可执行程序：npm install typescript 下面我们可以通过 tsc 命令将 typescript 代码转换为标准 JavaScript 代码。我们将上面示例中的代码保存为 demo.ts 文件，然后通过 npx 调用 tsc 命令(如果以 global 方式安装 typescript 则可以直接运行命令)：npx tsc demo.ts 以上命令会转换 JavaScript 代码并自动创建一个 demo.js 文件，此文件可以在 node.js 运行:function isAdult(user) { return user.age >= 18; } var justine = { name: 'Justine', age: 23 }; var isJustineAnAdult = isAdult(justine);如果 typescript 代码中包含错误，则使用上面的转换命令会出现报错信息：% npx tsc demo.ts demo.ts:12:5 - error TS2322: Type 'string' is not assignable to type 'number'. 12 age: '23', ~~ demo.ts:3:5 3 age: number; ~~ The expected type comes from property 'age' which is declared here on type 'User' Found 1 error.可以看到 typescript 成功的防止将包含潜在错误代码的程序发布出去。更多介绍除此之外 typescript 也包含了很多其他很好的工具，如：interfaces, classes, utility types 等。更多使用方法参考 typescript 官方文档：https://www.typescriptlang.org/docs。typescript 在 node.js 生态中已经有了很好的发展，且被用于很多开源项目和框架中。以下是一些使用 typescript 的开源项目：NestJS -…

node.js 入门教程之十三 -- environment，error handler 和 log object
type: item
date: 2021-02-20T09:19:00+00:00
description: environment 运行环境production environment 产品环境和 development environment 开发环境可以设置不同的配置。node.js 默认为 development env 开发环境。通过设置环境变量：NODE_ENV=production 可以告诉 node.js 当前为产品环境。Linux 中可以通过如下指令修改：export NODE_ENV=production 这样在当前 shell 中就会以 production 模式运行 node 程序，如果要永久修改此环境变量可以将命令写入 ~/.bashrc 文件。也可以在运行 node 程序时定义 NODE_ENV：NODE_ENV=production node app.js 设置 NODE_ENV 为 production 有如下优势：日志记录保持在最小 level更多的 caching 缓存级别来提高运行效率可以通过状态判读符来判断当前运行环境，执行不同指令：if (process.env.NODE_ENV === 'development') { console.log('running on development environment') } if (process.env.NODE_ENV === 'production') { console.log('running on production environment') } if (['production', 'staging'].indexOf(process.env.NODE_ENV) != -1) { console.log('running on production environment') } else { console.log('running on development environment') }上面第三个指令通过调用 array 的 indexOf 来获取某个字符串在数组中的索引号，如果不存在这个字符串则返回 -1。注意使用类型判断符=== 是为了保证类型和数值都匹配。error handling 错误处理node.js 中通过 exceptions 来处理 error 情况。可以通过 throw 创建一个 exception：const test = 123; throw test;当 JavaScript 执行到上面的 throw 时程序会立刻停止，然后控制权会交给最近的 exception handler 例外控制器。JavaScript 中 test 可以是任意的字符串，数字或者 object。但在 node.js 中只用来 throw Error objects。一个 error object 可以是 Error 的实例或者是 Error class 的 child class 的实例：throw new Error('error') class childError extends Error { } throw new childError('error')下面介绍几种 exception handler 处理器。第一种是通过 try/catch 声明来处理，在 try 中定义任意 exception 以及对应的 catch 块来响应：try { const a = 1; if (a == 0) { throw 'abc' } else { throw 123 } } catch (e) { console.log(e) }catch 的参数 e 就是对应 throw 的数据。如果在程序执行中，如果某个 throw 的 exception 没有被 catch，程序就会立刻 crash。可以通过监听 process 的 uncaughtException event 事件来解决这个问题：process.on('uncaughtException', err => { console.log('there was a uncaught error: ', err.message) process.exit(1) }) throw new Error('error')第二种是 promise 中的 error handler。使用 promise 可以将多个操作过程组合成链，在链的最后处理 error 情况，下面是一个简单示例：let a = 0; let b = 0; const doSomething1 = new Promise((resolve, reject) => { if (a == 0) resolve('resolve 1') else reject('reject 1') }) const doSo…

node.js 入门教程之十二 -- Streams 流
type: item
date: 2021-02-20T06:43:00+00:00
description: Streams 是驱动 node.js 程序的核心概念。它提供了处理对文件的读写，网络传输，或者其他端到端的数据交换的更加高效的方式。streams 流并不是 node.js 首先引入的概念，unix 操作系统在很久之前就在使用了，一个程序可以通过 pipe 管道操作符 | 来传递 streams 流给其他程序。下面的示例是在 Linux 中，通过 pipe 管道将 cat 读取的文件数据传递给 grep 进行过滤，test.txt 文件内容如下：aaa bbb bbb ccc aaa ccc$ cat test.txt | grep aaa aaa bbb aaa cccstream 优点在传统方法中，当程序读取一个文件内容时会先将文件内容全部读取到内存中，然后再去使用它。而使用 stream 时会一段段的读取文件内容并进行处理，而不需要整体读取到内存。node.js 的 stream 模块是构建所有 streaming API 的底层。所有的 streams 流都是 eventEmitter 的实例。每种类型的 stream 都有各自的 event 事件，在数据流变化时触发对应事件。相比于其他数据处理方法，streams 有两个优势：内存效率：在使用数据前不需要将大量数据存入内存中。时间效率：能够更快的进入数据处理阶段，在获取到文件后能够很快的使用它而不需要等待它全部加载完毕。使用方法下面我们从一个示例来说明 stream 的使用方法。我们建立一个 http server，当收到请求时读取一个本地文件的内容并作为 response 发送给客户端。首先传统实现方式如下：const http = require('http') const fs = require('fs') const hostname = '127.0.0.1' const port = 3000 const server = http.createServer((req, res) => { fs.readFile(__dirname + '/test.txt', (err, data) => { if (err) { console.log('read error') } res.end(data) }) }) server.listen(port, hostname, () => { console.log(`Server running at http://${hostname}:${port}/`) })以上方式在收到请求后，通过 readFile() 读取文件内容到内存中，当成功后会调用 callback 通过 res.end(data) 发送读取的数据给客户端。如果文件较大时以上过程会花费一些时间。__dirname 返回当前执行文件的路径。下面介绍使用 stream 实现上面的过程：const http = require('http') const fs = require('fs') const hostname = '127.0.0.1' const port = 3000 const server = http.createServer((req, res) => { const stream = fs.createReadStream(__dirname + '/test.txt') stream.pipe(res) }) server.listen(port, hostname, () => { console.log(`Server running at http://${hostname}:${port}/`) })通过 fs.createReadStream() 返回一个文件的 stream。不同于第一种，以上方式会在 stream 中只要有了 data chunk 块就立刻作为 response 数据传输给客户端。这样就会一边读取文件一边传输数据。pipe()以上示例中调用 stream 的 pipe() method。它的功能是建立一个 source stream 源流到 destination stream 目标流的 pipe 管道。这样文件的 stream 流通向了 http response。pipe() 的返回是 destination stream 目标流，这样就可以很方便的链接多个 pipe：src.pipe(dest1).pipe(dest2)以下写法和上面示例效果一样：src.pipe(dest1) dest1.pipe(dest2)基于 stream 的 node.js API由于 stream 的巨大优势，很多 node.js 核心模块提供了 stream 的原生支持，以下是常用的部分：proces…

node.js 入门教程之十一 -- buffers 缓存
type: item
date: 2021-02-16T09:26:00+00:00
description: 一个 buffer 缓存就是一个内存空间。JavaScript 开发者相较于 c++/go 等语言的开发者对内存的理解比较少。它表示在 JavaScript V8 engine 之外的分配的一块 fixed-size 固定大小的内存。可以想象 buffer 就是一个 integer 整型数组，每个元素代表一个 byte 的数据。node.js 中 buffer 通过 Buffer class 实现。为何需要 buffer引入 Buffer 是为了帮助开发者处理 binary 二进制数据。传统的 ecosystem 只可以处理 strings 字符串数据。Buffer 和 streams 是紧密关联的，当 stream processor 流处理器接收数据的速度大于其处理速度时，就会先将数据放在 buffer 中。可以想象下，在我们看在线视频时，在网络较好的情况下进度条中可以看到视频下载的进度是超过当前播放进度的，此时浏览器就会先 buffer 缓存数据后续按时间顺序播放出来。创建 buffer可以通过 Buffer.from(), Buffer.alloc() 和 Buffer.allocUnsafe() methods 来创建一个 buffer，下面依次介绍。使用 Buffer.from() 可以从 array 数组创建 buffer：const buf1 = Buffer.from('hey!') 使用 Buffer.from(string[, encoding]) 可以设置 string 的编码类型，Default: utf8：const buf2 = Buffer.from('7468697320697320612074c3a97374', 'hex') 也可以根据另一个 buffer：const buf3 = Buffer.from(buf1) 通过 Buffer.alloc() 或 Buffer.allocUnsafe() 来初始化一个指定大小的 buffer，单位为 byte 字节：const buf4 = Buffer.alloc(1024) const buf5 = Buffer.allocUnsafe(1024) 以上会创建一个 1kb 大小的 buffer。通过 alloc 创建的 buffer 会初始化其数据为 0，而 allocUnsafe 创建的 buffer 内的数据会是 uninitiated 未初始化的，这就表示使用 allocUnsafe 比 alloc 创建速度更快。但是通过 allocUnsafe 创建的 buffer 内存空间会包含有以前的数据可能引起潜在问题。使用 buffer我们可以像访问数组一样访问 buffer 数据：const buf1 = Buffer.from('hey!') console.log(buf1[0]) console.log(buf1[1]) console.log(buf1[2]) //output: //104 //101 //121这些输出的数字就是代表 buffer 中每个字符的 Unicode Code，h => 104, e => 101, y => 121。使用 toString() method 可以将 buffer 输出为字符：console.log(buf1) console.log(buf1.toString()) //output: //<Buffer 68 65 79 21> //hey!buf1.length 可以读取 buffer 长度：console.log(buf1.length) //output: //4也可以使用循环 iterate buffer 的数据：for (const iterator of buf1) { console.log(iterator) } //output: 104 101 121 33通过 write() method 可以给 buffer 写入数据：const buf6 = Buffer.alloc(5) buf6.write('hello')也可以像数组一样修改某个元素的数据：const buf6 = Buffer.alloc(5) buf6.write('hello') buf6[0] = 111 console.log(buf6.toString()) //output: //oello可以看到需要修改字符对应的 Unicode Code。使用 copy() method 可以复制 buffer 数据到其他 buffer：const buf1 = Buffer.from('hey!') const buf7 = Buffer.alloc(4) buf1.copy(buf7) cons…

node.js 入门教程之十 -- http 模块
type: item
date: 2021-02-15T06:01:00+00:00
description: http 模块是 node.js 处理 networking 网络的关键模块。在之前的章节我介绍了使用 http 模块建立 server 以及发起 GET/POST 请求的方法，可以参考：https://blog.niekun.net/archives/2137.htmlhttp 模块集成于 node.js 核心无需单独安装，使用下面命令引入模块：const http = require('http') 模块提供了很多 properties，methods 和 classes。Properties 属性http.METHODShttp.METHODS property 返回 http 所有的可用 method 列表：> console.log(require('http').METHODS) [ 'ACL', 'BIND', 'CHECKOUT', 'CONNECT', 'COPY', 'DELETE', 'GET', 'HEAD', 'LINK', 'LOCK', 'M-SEARCH', 'MERGE', 'MKACTIVITY', 'MKCALENDAR', 'MKCOL', 'MOVE', 'NOTIFY', 'OPTIONS', 'PATCH', 'POST', 'PRI', 'PROPFIND', 'PROPPATCH', 'PURGE', 'PUT', 'REBIND', 'REPORT', 'SEARCH', 'SOURCE', 'SUBSCRIBE', 'TRACE', 'UNBIND', 'UNLINK', 'UNLOCK', 'UNSUBSCRIBE' ]http.STATUS_CODEShttp.STATUS_CODES 返回所有的 http 状态码及描述的 list 列表：> console.log(require('http').STATUS_CODES) { '100': 'Continue', '101': 'Switching Protocols', '102': 'Processing', '103': 'Early Hints', '200': 'OK', '201': 'Created', '202': 'Accepted', '203': 'Non-Authoritative Information', '204': 'No Content', '205': 'Reset Content', '206': 'Partial Content', '207': 'Multi-Status', '208': 'Already Reported', '226': 'IM Used', '300': 'Multiple Choices', '301': 'Moved Permanently', '302': 'Found', '303': 'See Other', '304': 'Not Modified', '305': 'Use Proxy', '307': 'Temporary Redirect', '308': 'Permanent Redirect', '400': 'Bad Request', '401': 'Unauthorized', '402': 'Payment Required', '403': 'Forbidden', '404': 'Not Found', '405': 'Method Not Allowed', '406': 'Not Acceptable', '407': 'Proxy Authentication Required', '408': 'Request Timeout', '409': 'Conflict', '410': 'Gone', '411': 'Length Required', '412': 'Precondition Failed', '413': 'Payload Too Large', '414': 'URI Too Long', '415': 'Unsupported Media Type', '416': 'Range Not Satisfiable', '417': 'Expectation Failed', '418': "I'm a Teapot", '421': 'Misdirected Request', '422': 'Unprocessable Entity', '423': 'Locked', '424': 'Failed Dependency', '425': 'Too Early', '426': 'Upgrade Required', '428': 'Precondition Required', '429'…

node.js 入门教程之九 -- events 模块
type: item
date: 2021-02-08T13:42:00+00:00
description: 在之前介绍 event loop 的章节，我们简单介绍了 events 模块的使用，它可以设置并监听某个信号并在信号触发时响应 callback function。events 模块提供了 EventEmitter class 它是处理 events 的关键工具。引入及初始化 events 模块如下：const EventEmitter = require('events') const eventEmitter = new EventEmitter()每个 event listener 事件监听器都是互相独立的，同时它们使用如下 events：newListener 当一个 event 被添加removeListener 当一个 event 被删除下面介绍常用的一些 method。eventEmitter.addListener()eventEmitter.addListener() 和 eventEmitter.on() 功能相同。可以给某个 event 添加一个 listener，语法如下：eventEmitter.addListener('test', () => {}) eventEmitter.emit()eventEmitter.emit() 用来触发一个信号，信号触发后，对应信号的 event listener 会响应 callback function：eventEmitter.addListener('test', () => { console.log('test emited') }) eventEmitter.emit('test') //output: //test emitedeventEmitter.eventNames()eventEmitter.eventNames() 会返回当前 EventEmitter object 内注册已的 events 为一个字符串数组：eventEmitter.addListener('test1', () => {}) eventEmitter.addListener('test2', () => {}) console.log(eventEmitter.eventNames()) //output: //[ 'test1', 'test2' ]eventEmitter.getMaxListeners()eventEmitter.getMaxListeners() 返回当前 EventEmitter object 能添加的 event listener 最大数量。默认为 10 个，可以通过 setMaxListeners() 修改。eventEmitter.listenerCount()eventEmitter.listenerCount() 返回某个 event 定义的 listeners 数量：eventEmitter.addListener('test4', () => { console.log('a') }) eventEmitter.addListener('test4', () => { console.log('b') }) console.log(eventEmitter.listenerCount('test4')) //output: //2eventEmitter.listeners()eventEmitter.listeners() 返回某个 event 所有的 listeners 为一个数组：const f1 = () => {} const f2 = () => {} eventEmitter.addListener('test5', f1) eventEmitter.addListener('test5', f2) console.log(eventEmitter.listeners('test5')) //output: //[ [Function: f1], [Function: f2] ]eventEmitter.off()eventEmitter.off() 和 eventEmitter.removeListener() 功能相同。可以删除一个 event 的某个 listener，需要指定 event 名称和 listener callback 名称：const f3 = () => {} eventEmitter.addListener('test6', f3) eventEmitter.off('test6', f3)eventEmitter.on()eventEmitter.on() 同 eventEmitter.addListener() 功能相同。可以给某个 event 添加一个 listener：event…

node.js 入门教程之八 -- os 模块
type: item
date: 2021-02-08T06:03:00+00:00
description: os 模块可以用来获取操作系统的底层信息和系统上运行的程序，以及和系统交互。它集成在 node.js 中，只需要引入即可：const os = require('os') 这里先介绍一些在处理文件时有用的 properties：os.EOL 会返回 line delimiter 行定界符，\n on Linux and macOS, and \r\n on Windowsos.constants.signals 提供处理进程的信号常数，如：SIGHUP, SIGKILLos.constants.errno 提供报告 error 的相关常数，如：EADDRINUSE, EOVERFLOW完整的 signals 信号列表参考：https://nodejs.org/api/os.html#os_signal_constants下面介绍 os 模块常用的 method。os.arch()os.arch() 会返回底层架构的字符串，如：arm, x64, arm64：console.log(os.arch()) //output: x64os.cpus()os.cpus() 会返回系统的 cpu 信息：console.log(os.cpus()) //output: [ { model: 'Intel(R) Core(TM) i7-6500U CPU @ 2.50GHz', speed: 2591, times: { user: 1871810, nice: 17880, sys: 1451160, idle: 81134010, irq: 0 } }, { model: 'Intel(R) Core(TM) i7-6500U CPU @ 2.50GHz', speed: 2591, times: { user: 1838130, nice: 12780, sys: 1532060, idle: 81234660, irq: 0 } } ]os.endianness()根据 node.js 是通过 Big Endian 或 Little Endian 大尾数或小尾数编译的，os.endianness() 返回 BE 或 LE：console.log(os.endianness()) //output: LE关于 Big Endian 或 Little Endian 参考：https://en.wikipedia.org/wiki/Endiannessos.freemem()os.freemem() 返回系统可用内存，以字节为单位：console.log(os.freemem()) //output: 574537728os.homedir()os.homedir() 返回系统当前用户的 home 目录路径：console.log(os.homedir()) //output: /home/marcoos.hostname()os.hostname() 返回 hostname 信息：console.log(os.hostname()) //output: marco-virtual-machineos.loadavg()os.loadavg() 返回系统计算的平均加载时间，此 method 只在 Linux/macOS 下有意义：console.log(os.loadavg()) //output: [ 0.08, 0.17, 0.13 ]os.networkInterfaces()os.networkInterfaces() 返回系统的网卡信息：console.log(os.networkInterfaces()) //output: { lo0: [ { address: '127.0.0.1', netmask: '255.0.0.0', family: 'IPv4', mac: 'fe:82:00:00:00:00', internal: true }, { address: '::1', netmask: 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', family: 'IPv6', mac: 'fe:82:00:00:00:00', scopeid: 0, internal: true }, { address: 'fe80::1', netmask: 'ffff:ffff:ffff:ffff::', family: 'IPv6', mac: 'fe:82:00:00:00:00', scopeid: 1, internal: true } ], en1: [ { address: 'fe82::9b:8282:d7e6:496e', netmask: 'ffff:ffff:ffff:ff…

node.js 入门教程之七 -- fs 和 path 模块
type: item
date: 2021-02-08T03:21:00+00:00
description: 上一章节我们了解了如果访问文件系统。使用到了 fs 和 path 模块的部分功能，下面我们详细了解这两个模块可以实现的功能。fs module通过以上几个使用场景可以看到 fs 模块提供了很多有用的功能来对 file system 进行访问和交互。并且他是集成在 node.js core 中的，并不需要手动安装：const fs = require('fs') 通过以上方式引入 fs 模块后，我们就可以使用它的各种功能了。包括：fs.access(): 检查文件是否存在以及 node.js 是否有权限访问fs.appendFile(): 给一个文件附加数据，如果文件不存在则会创建它fs.chmod(): 修改文件的权限fs.chown(): 修改文件的 owner 和 group 设置fs.close(): 关闭已经打开的文件的 descriptorfs.copyFile(): 复制一个文件fs.createReadStream(): 创建一个可读的文件 streamfs.createWriteStream(): 创建一个可写的文件 streamfs.link(): 创建一个文件的硬链接fs.mkdir(): 新建文件夹fs.mkdtemp(): 创建一个临时文件夹fs.open(): 打开一个文件的 descriptor，可定义打开编码类型fs.readdir(): 读取一个目录下的内容fs.readFile(): 读取一个文件的内容fs.readlink(): 读取一个软连接的值fs.realpath(): resolve 查询一个相对路径文件的绝对路径fs.rename(): 重命名文件或文件夹fs.rmdir(): 删除一个文件夹fs.stat(): 返回一个文件的状态信息fs.symlink(): 创建一个文件的软链接fs.truncate(): 将一个文件分割成指定长度大小fs.unlink(): 删除一个文件或一个软链接fs.unwatchFile(): 停止监测对一个文件的修改fs.utimes(): 修改一个文件的时间戳fs.watchFile(): 开始监测对一个文件的修改fs.writeFile(): 对一个文件写入数据fs 模块的一个特性是它的以上所有 method 默认都是 asynchronous 异步的，给 method 后加上 Sync 就可以使用同步模式，例如：fs.rename() -> fs.renameSync()fs.write() -> fs.writeSync()同步模式下通过使用 try/catch 块来处理 error 情况。同步模式下程序会在处理文件过程中出现进程阻塞直到处理完成，在编程中可以根据需要灵活运用。path modulepath 模块也提供了很多实用的功能来访问文件系统以及交互。同样的它也是集成在 node.js core 中而不需要单独安装。const path = require('path') 通过 path.sep 可以获取当前系统的 path separator 路径分隔符：\ on Windows, and / on Linux/macOS。通过 path.delimiter 可以获取当前系统的 path delimiter 路径定界符：; on Windows, and : on Linux / macOS。例如在 Linux 下：console.log(process.env.PATH); // Prints: '/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin' process.env.PATH.split(path.delimiter); // Returns: ['/usr/bin', '/bin', '/usr/sbin', '/sbin', '/usr/local/bin']在 Windows 下：console.log(process.env.PATH); // Prints: 'C:\Windows\system32;C:\Windows;C:\Program Files\node\' process.env.PATH.split(path.delimiter); // Returns ['C:\\Windows\\system32', 'C:\\Windows', 'C:\\Program Files\\node\\']下面是 path 的常用 method 介绍。path.basename()返回路径上的最后一部分，通过第二个参数可以过滤掉扩展名：console.log(path.basename('/home/marco/dir')) console.log(path.basename('…

node.js 入门教程之六 -- FileSystem
type: item
date: 2021-02-07T09:17:00+00:00
description: File descriptor 文件描述器当需要访问文件系统里的某个文件时，需要首先得到这个文件的 file descriptor 文件描述器。一个 file descriptor 就是通过 fs 模块的 open() method 打开对应文件的返回数据。它的结构如下：const fs = require('fs') fs.open('./test.txt', 'r', (err, data) => { if (err) { console.log('open fail') return } console.log('open success') })如果文件打开成功，callback 中的 data 数据就是一个 file descriptor。注意并不是文件的内容。这种方式是异步处理的，文件打开过程中系统会执行其他任务。在上面的示例中，fs.open() 的第三个参数使用了 'f' 标记，它的意思就是以只读模式打开文件。我们可以通过使用不同的标记来以不同的方式打开文件：r 只读模式，文件不存在会报错a 只写模式，streaming 流定位到文件结尾位置，如果文件不存在则创建文件r+ 读写模式，文件不存在会报错w+ 读写模式，streaming 流定位到文件起始位置，如果文件不存在则创建文件a+ 读写模式，streaming 流定位到文件结尾位置，如果文件不存在则创建文件更多 flag 的使用参考：https://nodejs.org/api/fs.html#fs_file_system_flags也可以使用 fs.openSync() method 打开文件，其返回值为 file descriptor 而不是通过 callback 的方式：try { const data = fs.openSync('./test.txt', 'r') console.log('open success') } catch (error) { console.log('open fail') }这种方式是同步模式的，程序会等待接收到 file descriptor 或者 error 后才执行后面的指令。File stats 文件信息每个文件都包含独自的属性信息，可以通过 node.js 查看。通常我们使用 fs 模块的 stat() method。以下是一个简单示例：const fs = require('fs') fs.stat('./test.txt', (err, stats) => { if (err) { console.log('read fail') return } console.log(stats) })同样的，node.js 也提供了同步模式 method，在读取 stats 过程中会阻塞进程知道读取结束：try { const stats = fs.statSync('./test.txt') console.log(stats) } catch (error) { console.log(error) }文件信息存储在 stats 变量中，可以读取其中的需要的信息，常用的有以下：是否是文件或文件夹：stats.isFile()，stats.isDirectory()是否是一个链接文件：stats.isSymbolicLink()文件大小(单位为字节)：stats.sizeFile Path 文件路径每个文件在系统中都有一个路径。在 Linux 中路径格式如：/users/joe/file.txt，在 Windows 中路径格式如：C:\users\joe\file.txt。在程序中引用路径时需要特别注意路径的格式。node.js 中可以使用 path 模块来处理文件路径相关数据。如果给定一个路径，可以提取其相关数据，如：path.dirname: 获取文件的上级文件夹路径path.basename: 获取文件名部分path.extname: 获取文件扩展名const path = require('path') const file = '/home/marco/file.txt' console.log(path.dirname(file)) console.log(path.basename(file)) console.log(path.extname(file)) //output: /home/marco file.txt .txt可以获取文件名不包含扩展名部分，通过给 path.basename() 设置第二个参数：console.log(path.basename(file, path.extname(file))) //output: file可以组合多个部分为一个 path：const dir…

node.js 入门教程之五 -- HTTP request and response
type: item
date: 2021-02-06T08:50:00+00:00
description: 搭建 http server下面是一个 http server 的示例：const http = require('http') process.env.PORT = 3000 const port = process.env.PORT const server = http.createServer((req, res) => { res.statusCode = 200 res.setHeader('Content-Type', 'text/html') res.end(' hello world ') }) server.listen(port, () => console.log(`server running at port ${port}`))访问 http://localhost:3000 就会看到页面显示 hello world。下面我们简单解释下以上示例的执行过程：首先引入 http module设置环境变量 PORT 为 3000，然后赋值给 port 变量创建一个 server，其中含有一个 callback function，用户访问 server 时会被调用设置监听端口并启动 server，当 server 启动成功后，会执行 server.listen 内定义的 callback function当 server 收到用户的请求时，会触发 request event 并提供了两个数据：一个 request(http.IncomingMessage object) 和一个 response(http.ServerResponse object)。request 提供了 request 的详细信息，可以用来获取请求 hreaders 和请求数据。response 用来操作将要发送给客户端的数据。在上面的示例中，我们修改了 response 相关信息：设置 statusCode 为 200设置一个 header结束 response 并将响应内容作为数据传入 end()发起 GET 请求下面示例发起一个 GET 请求：const https = require('https') const options = { hostname: 'niekun.net', port: 443, path: '/', method: 'GET' } const req = https.request(options, res => { console.log(`statusCode is：${res.statusCode}`); res.on('data', d => { process.stdout.write(d) }) }) req.on('error', err => console.log(err)) req.end()以上示例执行过程为：首先引入 https 模块。创建一个 options object 存储请求的信息。创建一个 https request，使用 options 作为请求信息，当请求成功后会执行后面定义的 callback function。定义一个 event handler 事件管理器来处理当 error 被触发时的响应。最后结束这个 request发起 POST 请求下面是一个 POST 请求示例：const https = require('https') const data = JSON.stringify({ todo: 'go to sleep' }) const options = { hostname: 'niekun.net', port: 443, path: '/', method: 'POST', Headers: { 'Content-Type': 'application/json', 'Content-Length': data.length } } const req = https.request(options, res => { console.log(`statusCode is：${res.statusCode}`); res.on('data', d => { process.stdout.write(d) }) }) req.on('error', err => console.log(err)) req.write(data) req.end()POST 请求相比 GET 请求多了 request.write(data) 部分，因为 POST 是把数据消息放在 body 中的。PUT 和 DELETE 请求和 POST 格式相同，只需要修改 request.method 即可。使用 Axios 库发起 POST 请求上面介绍了是应用 htt…