- Node.js 教程
- Node.js - 首页
- Node.js - 简介
- Node.js - 环境搭建
- Node.js - 第一个应用程序
- Node.js - REPL 终端
- Node.js - 命令行选项
- Node.js - 包管理器 (NPM)
- Node.js - 回调函数概念
- Node.js - 上传文件
- Node.js - 发送邮件
- Node.js - 事件
- Node.js - 事件循环
- Node.js - 事件发射器
- Node.js - 调试器
- Node.js - 全局对象
- Node.js - 控制台
- Node.js - 进程
- Node.js - 应用程序扩展
- Node.js - 打包
- Node.js - Express 框架
- Node.js - RESTful API
- Node.js - 缓冲区
- Node.js - 流
- Node.js - 文件系统
- Node.js MySQL
- Node.js - MySQL 入门
- Node.js - MySQL 创建数据库
- Node.js - MySQL 创建表
- Node.js - MySQL 插入数据
- Node.js - MySQL 查询数据
- Node.js - MySQL 条件查询
- Node.js - MySQL 排序
- Node.js - MySQL 删除数据
- Node.js - MySQL 更新数据
- Node.js - MySQL 连接查询
- Node.js MongoDB
- Node.js - MongoDB 入门
- Node.js - MongoDB 创建数据库
- Node.js - MongoDB 创建集合
- Node.js - MongoDB 插入数据
- Node.js - MongoDB 查找数据
- Node.js - MongoDB 查询
- Node.js - MongoDB 排序
- Node.js - MongoDB 删除数据
- Node.js - MongoDB 更新数据
- Node.js - MongoDB 数据限制
- Node.js - MongoDB 连接查询
- Node.js 模块
- Node.js - 模块
- Node.js - 内置模块
- Node.js - 实用工具模块
- Node.js - Web 模块
- Node.js 有用资源
- Node.js 快速指南
- Node.js - 有用资源
- Node.js - 讨论
Node.js 快速指南
Node.js - 简介
什么是 Node.js?
Node.js 是一个基于 Google Chrome 的 JavaScript 引擎 (V8 引擎) 的服务器端平台。Node.js 由 Ryan Dahl 于 2009 年开发,最新版本为 v0.10.36。根据其官方文档提供的定义如下:
Node.js 是一个基于Chrome 的 JavaScript 运行时环境构建的平台,用于轻松构建快速且可扩展的网络应用程序。Node.js 使用事件驱动、非阻塞 I/O 模型,使其轻量且高效,非常适合运行在分布式设备上的数据密集型实时应用程序。
Node.js 是一个开源的、跨平台的运行时环境,用于开发服务器端和网络应用程序。Node.js 应用程序是用 JavaScript 编写的,可以在 OS X、Microsoft Windows 和 Linux 上的 Node.js 运行时环境中运行。
Node.js 还提供丰富的各种 JavaScript 模块库,在很大程度上简化了使用 Node.js 开发 Web 应用程序的过程。
Node.js = Runtime Environment + JavaScript Library
Node.js 的特性
以下是使 Node.js 成为软件架构师首选的一些重要特性。
异步和事件驱动 - Node.js 库的所有 API 都是异步的,即非阻塞的。它本质上意味着基于 Node.js 的服务器永远不会等待 API 返回数据。服务器在调用 API 后会移动到下一个 API,而 Node.js 的事件通知机制帮助服务器从之前的 API 调用中获取响应。
非常快 - Node.js 库构建于 Google Chrome 的 V8 JavaScript 引擎之上,代码执行速度非常快。
单线程但高度可扩展 - Node.js 使用单线程模型和事件循环。事件机制帮助服务器以非阻塞方式响应,并使服务器具有高度可扩展性,这与创建有限线程来处理请求的传统服务器相反。Node.js 使用单线程程序,并且同一个程序可以比 Apache HTTP Server 等传统服务器提供服务给更多数量的请求。
无缓冲 - Node.js 应用程序从不缓冲任何数据。这些应用程序只是分块输出数据。
许可证 - Node.js 在MIT 许可证下发布。
谁在使用 Node.js?
以下是 github wiki 上的链接,其中包含使用 Node.js 的项目、应用程序和公司的详尽列表。此列表包括 eBay、通用电气、GoDaddy、微软、PayPal、Uber、Wikipins、雅虎和 Yammer 等。
概念
下图描述了 Node.js 的一些重要部分,我们将在后续章节中详细讨论。
在哪里使用 Node.js?
以下是 Node.js 证明自己是完美技术合作伙伴的领域。
- I/O 密集型应用程序
- 数据流应用程序
- 数据密集型实时应用程序 (DIRT)
- 基于 JSON API 的应用程序
- 单页应用程序
在哪里不使用 Node.js?
对于 CPU 密集型应用程序,不建议使用 Node.js。
Node.js - 环境搭建
在线试用选项
您实际上不需要设置自己的环境来开始学习 Node.js。原因很简单,我们已经在网上设置了 Node.js 环境,这样您就可以在线执行所有可用的示例并通过实践学习。随意修改任何示例并使用不同的选项检查结果。
使用以下示例,使用以下示例代码框(在我们的网站上)右上角的实时演示选项:
/* Hello World! program in Node.js */ console.log("Hello World!");对于本教程中给出的大多数示例,您都会找到一个“试用”选项,因此只需使用它即可享受学习的乐趣。
本地环境搭建
如果您仍然愿意为 Node.js 设置环境,则需要在您的计算机上安装以下两个软件:(a)文本编辑器和(b)Node.js 二进制安装程序。
文本编辑器
这将用于键入您的程序。一些编辑器的示例包括 Windows 记事本、OS Edit 命令、Brief、Epsilon、EMACS 和 vim 或 vi。
文本编辑器的名称和版本在不同的操作系统上可能会有所不同。例如,Windows 上将使用记事本,而 vim 或 vi 也可以在 Windows 以及 Linux 或 UNIX 上使用。
您使用编辑器创建的文件称为源文件,其中包含程序源代码。Node.js 程序的源文件通常以扩展名“.js”命名。
在开始编程之前,请确保您已安装一个文本编辑器,并且您有足够的经验来编写计算机程序,将其保存在文件中,最后执行它。
Node.js 运行时
在源文件中编写的源代码只是 JavaScript。Node.js 解释器将用于解释和执行您的 JavaScript 代码。
Node.js 发行版作为 SunOS、Linux、Mac OS X 和 Windows 操作系统的二进制安装程序提供,具有 32 位 (386) 和 64 位 (amd64) x86 处理器架构。
以下部分指导您如何在各种操作系统上安装 Node.js 二进制发行版。
下载 Node.js 存档
从Node.js 下载下载最新版本的 Node.js 可安装存档文件。在撰写本教程时,不同操作系统上可用的版本如下。
操作系统 | 存档名称 |
---|---|
Windows | node-v6.3.1-x64.msi |
Linux | node-v6.3.1-linux-x86.tar.gz |
Mac | node-v6.3.1-darwin-x86.tar.gz |
SunOS | node-v6.3.1-sunos-x86.tar.gz |
在 UNIX/Linux/Mac OS X 和 SunOS 上安装
根据您的操作系统架构,下载并将存档 node-v6.3.1-**osname**.tar.gz 解压缩到 /tmp,然后最终将解压缩的文件移动到 /usr/local/nodejs 目录。例如
$ cd /tmp $ wget https://node.org.cn/dist/v6.3.1/node-v6.3.1-linux-x64.tar.gz $ tar xvfz node-v6.3.1-linux-x64.tar.gz $ mkdir -p /usr/local/nodejs $ mv node-v6.3.1-linux-x64/* /usr/local/nodejs
将 /usr/local/nodejs/bin 添加到 PATH 环境变量。
操作系统 | 输出 |
---|---|
Linux | export PATH=$PATH:/usr/local/nodejs/bin |
Mac | export PATH=$PATH:/usr/local/nodejs/bin |
FreeBSD | export PATH=$PATH:/usr/local/nodejs/bin |
在 Windows 上安装
使用 MSI 文件并按照提示安装 Node.js。默认情况下,安装程序使用 C:\Program Files\nodejs 中的 Node.js 发行版。安装程序应在 Windows 的 PATH 环境变量中设置 C:\Program Files\nodejs\bin 目录。重新启动任何打开的命令提示符以使更改生效。
验证安装:执行文件
在您的机器(Windows 或 Linux)上创建一个名为main.js的 js 文件,其中包含以下代码。
/* Hello, World! program in node.js */ console.log("Hello, World!")
现在使用 Node.js 解释器执行 main.js 文件以查看结果:
$ node main.js
如果您的安装一切正常,则应产生以下结果:
Hello, World!
Node.js - 第一个应用程序
在使用 Node.js 创建实际的“Hello, World!”应用程序之前,让我们看看 Node.js 应用程序的组件。Node.js 应用程序包含以下三个重要组件:
导入所需的模块 - 我们使用require指令加载 Node.js 模块。
创建服务器 - 一个服务器,它将监听客户端的请求,类似于 Apache HTTP 服务器。
读取请求并返回响应 - 在前面步骤中创建的服务器将读取客户端(可以是浏览器或控制台)发出的 HTTP 请求并返回响应。
创建 Node.js 应用程序
步骤 1 - 导入所需的模块
我们使用require指令加载 http 模块并将返回的 HTTP 实例存储到 http 变量中,如下所示:
var http = require("http");
步骤 2 - 创建服务器
我们使用创建的 http 实例并调用http.createServer()方法来创建一个服务器实例,然后我们使用与服务器实例关联的listen方法将其绑定到 8081 端口。向其传递一个带有 request 和 response 参数的函数。编写始终返回“Hello World”的示例实现。
http.createServer(function (request, response) { // Send the HTTP header // HTTP Status: 200 : OK // Content Type: text/plain response.writeHead(200, {'Content-Type': 'text/plain'}); // Send the response body as "Hello World" response.end('Hello World\n'); }).listen(8081); // Console will print the message console.log('Server running at http://127.0.0.1:8081/');
以上代码足以创建一个 HTTP 服务器,该服务器监听,即等待本地机器 8081 端口上的请求。
步骤 3 - 测试请求和响应
让我们将步骤 1 和 2 组合到一个名为main.js的文件中,并启动我们的 HTTP 服务器,如下所示:
var http = require("http"); http.createServer(function (request, response) { // Send the HTTP header // HTTP Status: 200 : OK // Content Type: text/plain response.writeHead(200, {'Content-Type': 'text/plain'}); // Send the response body as "Hello World" response.end('Hello World\n'); }).listen(8081); // Console will print the message console.log('Server running at http://127.0.0.1:8081/');
现在执行 main.js 以启动服务器,如下所示:
$ node main.js
验证输出。服务器已启动。
Server running at http://127.0.0.1:8081/
向 Node.js 服务器发出请求
在任何浏览器中打开 http://127.0.0.1:8081/ 并观察以下结果。
恭喜,您已经启动并运行了第一个 HTTP 服务器,它正在响应 8081 端口上的所有 HTTP 请求。
Node.js - REPL 终端
REPL 代表 Read Eval Print Loop(读取-求值-打印-循环),它代表一个类似于 Windows 控制台或 Unix/Linux shell 的计算机环境,其中输入命令,系统以交互模式响应输出。Node.js 或Node捆绑了 REPL 环境。它执行以下任务:
读取 - 读取用户的输入,将输入解析为 JavaScript 数据结构,并将其存储在内存中。
求值 - 获取并计算数据结构。
打印 - 打印结果。
循环 - 循环执行上述命令,直到用户按下ctrl-c两次。
Node 的 REPL 功能在试验 Node.js 代码和调试 JavaScript 代码方面非常有用。
在线REPL终端
为了简化您的学习,我们设置了一个易于使用的在线Node.js REPL环境,您可以在其中练习Node.js语法 − 启动Node.js REPL终端
启动REPL
只需在shell/控制台中运行node(无需任何参数)即可启动REPL,如下所示。
$ node
您将看到REPL命令提示符>,您可以在其中键入任何Node.js命令 −
$ node >
简单表达式
让我们在Node.js REPL命令提示符下尝试一个简单的数学运算 −
$ node > 1 + 3 4 > 1 + ( 2 * 3 ) - 4 3 >
使用变量
您可以使用变量来存储值并在稍后打印,就像任何传统的脚本一样。如果未使用var关键字,则值将存储在变量中并打印出来。而如果使用var关键字,则值将被存储但不会被打印。您可以使用console.log()打印变量。
$ node > x = 10 10 > var y = 10 undefined > x + y 20 > console.log("Hello World") Hello World undefined
多行表达式
Node REPL支持与JavaScript类似的多行表达式。让我们检查一下以下do-while循环的运行情况 −
$ node > var x = 0 undefined > do { ... x++; ... console.log("x: " + x); ... } while ( x < 5 ); x: 1 x: 2 x: 3 x: 4 x: 5 undefined >
当您在开括号后按Enter键时,...会自动出现。Node会自动检查表达式的连续性。
下划线变量
您可以使用下划线(_)来获取最后一个结果 −
$ node > var x = 10 undefined > var y = 20 undefined > x + y 30 > var sum = _ undefined > console.log(sum) 30 undefined >
REPL命令
ctrl + c − 终止当前命令。
两次ctrl + c − 终止Node REPL。
ctrl + d − 终止Node REPL。
向上/向下键 − 查看命令历史记录并修改之前的命令。
Tab键 − 当前命令列表。
.help − 所有命令的列表。
.break − 从多行表达式退出。
.clear − 从多行表达式退出。
.save 文件名 − 将当前Node REPL会话保存到文件。
.load 文件名 − 在当前Node REPL会话中加载文件内容。
停止REPL
如上所述,您需要使用两次ctrl-c才能退出Node.js REPL。
$ node > (^C again to quit) >
Node.js - NPM
Node包管理器(NPM)提供两个主要功能 −
Node.js包/模块的在线存储库,可在search.nodejs.org上搜索
命令行实用程序,用于安装Node.js包,进行版本管理和Node.js包的依赖项管理。
v0.6.3版本之后,NPM与Node.js安装程序捆绑在一起。要验证这一点,请打开控制台并键入以下命令并查看结果 −
$ npm --version 2.7.1
如果您运行的是旧版本的NPM,则将其更新到最新版本非常容易。只需从根目录使用以下命令 −
$ sudo npm install npm -g /usr/bin/npm -> /usr/lib/node_modules/npm/bin/npm-cli.js [email protected] /usr/lib/node_modules/npm
使用NPM安装模块
安装任何Node.js模块都采用简单的语法 −
$ npm install <Module Name>
例如,以下是安装一个名为express的著名Node.js Web框架模块的命令 −
$ npm install express
现在您可以像下面这样在您的js文件中使用此模块 −
var express = require('express');
全局安装与本地安装
默认情况下,NPM以本地模式安装任何依赖项。这里的本地模式是指在Node应用程序所在的文件夹中的node_modules目录中安装包。本地部署的包可以通过require()方法访问。例如,当我们安装express模块时,它在安装express模块的当前目录中创建了node_modules目录。
$ ls -l total 0 drwxr-xr-x 3 root root 20 Mar 17 02:23 node_modules
或者,您可以使用npm ls命令列出所有已本地安装的模块。
全局安装的包/依赖项存储在系统目录中。此类依赖项可以在任何Node.js的CLI(命令行界面)函数中使用,但不能直接在Node应用程序中使用require()导入。现在让我们尝试使用全局安装来安装express模块。
$ npm install express -g
这将产生类似的结果,但模块将被全局安装。这里,第一行显示模块版本及其安装位置。
[email protected] /usr/lib/node_modules/express ├── [email protected] ├── [email protected] ├── [email protected] ├── [email protected] ├── [email protected] ├── [email protected] ├── [email protected] ├── [email protected] ├── [email protected] ├── [email protected] ├── [email protected] ├── [email protected] ├── [email protected] ├── [email protected] ├── [email protected] ├── [email protected] ├── [email protected] ([email protected]) ├── [email protected] ([email protected]) ├── [email protected] ([email protected]) ├── [email protected] ([email protected], [email protected]) ├── [email protected] ([email protected], [email protected], [email protected]) ├── [email protected] ([email protected]) ├── [email protected] ([email protected], [email protected]) └── [email protected] ([email protected], [email protected])
您可以使用以下命令检查所有全局安装的模块 −
$ npm ls -g
使用package.json
package.json位于任何Node应用程序/模块的根目录中,用于定义包的属性。让我们打开位于node_modules/express/中的express包的package.json
{ "name": "express", "description": "Fast, unopinionated, minimalist web framework", "version": "4.11.2", "author": { "name": "TJ Holowaychuk", "email": "[email protected]" }, "contributors": [{ "name": "Aaron Heckmann", "email": "[email protected]" }, { "name": "Ciaran Jessup", "email": "[email protected]" }, { "name": "Douglas Christopher Wilson", "email": "[email protected]" }, { "name": "Guillermo Rauch", "email": "[email protected]" }, { "name": "Jonathan Ong", "email": "[email protected]" }, { "name": "Roman Shtylman", "email": "[email protected]" }, { "name": "Young Jae Sim", "email": "[email protected]" } ], "license": "MIT", "repository": { "type": "git", "url": "https://github.com/strongloop/express" }, "homepage": "https://express.js.cn/", "keywords": [ "express", "framework", "sinatra", "web", "rest", "restful", "router", "app", "api" ], "dependencies": { "accepts": "~1.2.3", "content-disposition": "0.5.0", "cookie-signature": "1.0.5", "debug": "~2.1.1", "depd": "~1.0.0", "escape-html": "1.0.1", "etag": "~1.5.1", "finalhandler": "0.3.3", "fresh": "0.2.4", "media-typer": "0.3.0", "methods": "~1.1.1", "on-finished": "~2.2.0", "parseurl": "~1.3.0", "path-to-regexp": "0.1.3", "proxy-addr": "~1.0.6", "qs": "2.3.3", "range-parser": "~1.0.2", "send": "0.11.1", "serve-static": "~1.8.1", "type-is": "~1.5.6", "vary": "~1.0.0", "cookie": "0.1.2", "merge-descriptors": "0.0.2", "utils-merge": "1.0.0" }, "devDependencies": { "after": "0.8.1", "ejs": "2.1.4", "istanbul": "0.3.5", "marked": "0.3.3", "mocha": "~2.1.0", "should": "~4.6.2", "supertest": "~0.15.0", "hjs": "~0.0.6", "body-parser": "~1.11.0", "connect-redis": "~2.2.0", "cookie-parser": "~1.3.3", "express-session": "~1.10.2", "jade": "~1.9.1", "method-override": "~2.3.1", "morgan": "~1.5.1", "multiparty": "~4.1.1", "vhost": "~3.0.0" }, "engines": { "node": ">= 0.10.0" }, "files": [ "LICENSE", "History.md", "Readme.md", "index.js", "lib/" ], "scripts": { "test": "mocha --require test/support/env --reporter spec --bail --check-leaks test/ test/acceptance/", "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --require test/support/env --reporter dot --check-leaks test/ test/acceptance/", "test-tap": "mocha --require test/support/env --reporter tap --check-leaks test/ test/acceptance/", "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --require test/support/env --reporter spec --check-leaks test/ test/acceptance/" }, "gitHead": "63ab25579bda70b4927a179b580a9c580b6c7ada", "bugs": { "url": "https://github.com/strongloop/express/issues" }, "_id": "[email protected]", "_shasum": "8df3d5a9ac848585f00a0777601823faecd3b148", "_from": "express@*", "_npmVersion": "1.4.28", "_npmUser": { "name": "dougwilson", "email": "[email protected]" }, "maintainers": [{ "name": "tjholowaychuk", "email": "[email protected]" }, { "name": "jongleberry", "email": "[email protected]" }, { "name": "shtylman", "email": "[email protected]" }, { "name": "dougwilson", "email": "[email protected]" }, { "name": "aredridel", "email": "[email protected]" }, { "name": "strongloop", "email": "[email protected]" }, { "name": "rfeng", "email": "[email protected]" }], "dist": { "shasum": "8df3d5a9ac848585f00a0777601823faecd3b148", "tarball": "https://registry.npmjs.org/express/-/express-4.11.2.tgz" }, "directories": {}, "_resolved": "https://registry.npmjs.org/express/-/express-4.11.2.tgz", "readme": "ERROR: No README data found!" }
Package.json的属性
name − 包的名称
version − 包的版本
description − 包的描述
homepage − 包的主页
author − 包的作者
contributors − 包的贡献者姓名
dependencies − 依赖项列表。NPM会自动安装此处在包的node_module文件夹中提到的所有依赖项。
repository − 包的存储库类型和URL
main − 包的入口点
keywords − 关键词
卸载模块
使用以下命令卸载Node.js模块。
$ npm uninstall express
NPM卸载包后,您可以查看/node_modules/目录的内容或键入以下命令来验证它 −
$ npm ls
更新模块
更新package.json并更改要更新的依赖项的版本,然后运行以下命令。
$ npm update express
搜索模块
使用NPM搜索包名。
$ npm search express
创建模块
创建模块需要生成package.json。让我们使用NPM生成package.json,这将生成package.json的基本框架。
$ npm init This utility will walk you through creating a package.json file. It only covers the most common items, and tries to guess sane defaults. See 'npm help json' for definitive documentation on these fields and exactly what they do. Use 'npm install <pkg> --save' afterwards to install a package and save it as a dependency in the package.json file. Press ^C at any time to quit. name: (webmaster)
您需要提供有关模块的所有必要信息。您可以参考上面提到的package.json文件来了解各种所需信息的含义。生成package.json后,使用以下命令使用有效的电子邮件地址在NPM存储库网站上注册自己。
$ npm adduser Username: mcmohd Password: Email: (this IS public) [email protected]
现在是发布您的模块的时候了 −
$ npm publish
如果您的模块一切正常,它将发布到存储库中,并且可以使用NPM像任何其他Node.js模块一样安装。
Node.js - 回调函数概念
什么是回调?
回调是函数的异步等效项。在完成给定任务后调用回调函数。Node大量使用回调。Node的所有API都是以支持回调的方式编写的。
例如,读取文件的函数可能会开始读取文件并立即将控制权返回给执行环境,以便可以执行下一条指令。文件I/O完成后,它将调用回调函数,同时将回调函数、文件的内容作为参数传递。因此,不会阻塞或等待文件I/O。这使得Node.js具有高度的可扩展性,因为它可以处理大量请求而无需等待任何函数返回结果。
阻塞代码示例
创建一个名为input.txt的文本文件,内容如下 −
Tutorials Point is giving self learning content to teach the world in simple and easy way!!!!!
创建一个名为main.js的js文件,代码如下 −
var fs = require("fs"); var data = fs.readFileSync('input.txt'); console.log(data.toString()); console.log("Program Ended");
现在运行main.js查看结果 −
$ node main.js
验证输出。
Tutorials Point is giving self learning content to teach the world in simple and easy way!!!!! Program Ended
非阻塞代码示例
创建一个名为input.txt的文本文件,内容如下。
Tutorials Point is giving self learning content to teach the world in simple and easy way!!!!!
更新main.js,使其包含以下代码 −
var fs = require("fs"); fs.readFile('input.txt', function (err, data) { if (err) return console.error(err); console.log(data.toString()); }); console.log("Program Ended");
现在运行main.js查看结果 −
$ node main.js
验证输出。
Program Ended Tutorials Point is giving self learning content to teach the world in simple and easy way!!!!!
这两个示例解释了阻塞和非阻塞调用的概念。
第一个示例表明,程序会阻塞,直到读取文件,然后才继续结束程序。
第二个示例表明,程序不会等待文件读取并继续打印“程序结束”,同时,程序在不阻塞的情况下继续读取文件。
因此,阻塞程序的执行非常顺序。从编程的角度来看,实现逻辑更容易,但非阻塞程序的执行并非顺序的。如果程序需要使用任何要处理的数据,则应将其保存在同一个块中以使其顺序执行。
Node.js - 事件循环
Node.js是一个单线程应用程序,但它可以通过事件和回调的概念来支持并发。Node.js的每个API都是异步的,并且是单线程的,它们使用异步函数调用来维护并发。Node使用观察者模式。Node线程保持一个事件循环,每当任务完成时,它就会触发相应的事件,该事件会向事件监听器函数发出执行信号。
事件驱动编程
Node.js大量使用事件,这也是Node.js与其他类似技术相比速度很快的原因之一。Node启动其服务器后,它只需初始化其变量,声明函数,然后简单地等待事件发生。
在事件驱动的应用程序中,通常有一个主循环监听事件,然后在检测到这些事件之一时触发回调函数。
虽然事件看起来与回调非常相似,但区别在于回调函数是在异步函数返回其结果时调用的,而事件处理基于观察者模式。监听事件的函数充当观察者。每当触发事件时,其监听器函数就会开始执行。Node.js通过events模块和EventEmitter类提供了多个内置事件,它们用于绑定事件和事件监听器,如下所示 −
// Import events module var events = require('events'); // Create an eventEmitter object var eventEmitter = new events.EventEmitter();
以下是将事件处理程序与事件绑定的语法 −
// Bind event and event handler as follows eventEmitter.on('eventName', eventHandler);
我们可以通过编程方式触发事件,如下所示 −
// Fire an event eventEmitter.emit('eventName');
示例
创建一个名为main.js的js文件,代码如下 −
// Import events module var events = require('events'); // Create an eventEmitter object var eventEmitter = new events.EventEmitter(); // Create an event handler as follows var connectHandler = function connected() { console.log('connection succesful.'); // Fire the data_received event eventEmitter.emit('data_received'); } // Bind the connection event with the handler eventEmitter.on('connection', connectHandler); // Bind the data_received event with the anonymous function eventEmitter.on('data_received', function() { console.log('data received succesfully.'); }); // Fire the connection event eventEmitter.emit('connection'); console.log("Program Ended.");
现在让我们尝试运行上述程序并检查其输出 −
$ node main.js
它应该产生以下结果 −
connection successful. data received successfully. Program Ended.
Node应用程序如何工作?
在Node应用程序中,任何异步函数都接受回调作为最后一个参数,回调函数接受错误作为第一个参数。让我们再次回顾之前的示例。创建一个名为input.txt的文本文件,内容如下。
Tutorials Point is giving self learning content to teach the world in simple and easy way!!!!!
创建一个名为main.js的js文件,包含以下代码 −
var fs = require("fs"); fs.readFile('input.txt', function (err, data) { if (err) { console.log(err.stack); return; } console.log(data.toString()); }); console.log("Program Ended");
这里fs.readFile()是一个异步函数,其目的是读取文件。如果在读取操作期间发生错误,则err对象将包含相应的错误,否则data将包含文件的内容。readFile在读取操作完成后将err和data传递给回调函数,最后打印内容。
Program Ended Tutorials Point is giving self learning content to teach the world in simple and easy way!!!!!
Node.js - 事件发射器
Node中的许多对象都会发出事件,例如,net.Server在每次对等体连接到它时都会发出一个事件,fs.readStream在文件打开时会发出一个事件。所有发出事件的对象都是events.EventEmitter的实例。
EventEmitter类
正如我们在上一节中看到的,EventEmitter类位于events模块中。可以通过以下代码访问它 −
// Import events module var events = require('events'); // Create an eventEmitter object var eventEmitter = new events.EventEmitter();
当EventEmitter实例遇到任何错误时,它会发出'error'事件。添加新的监听器时,会触发'newListener'事件;移除监听器时,会触发'removeListener'事件。
EventEmitter提供了多个属性,例如on和emit。on属性用于将函数与事件绑定,emit用于触发事件。
方法
序号 | 方法和描述 |
---|---|
1 |
addListener(event, listener) 为指定的事件在监听器数组的末尾添加一个监听器。不会进行任何检查以查看是否已添加监听器。多次调用传递相同组合的事件和监听器将导致监听器被多次添加。返回发射器,因此可以链接调用。 |
2 |
on(event, listener) 为指定的事件在监听器数组的末尾添加一个监听器。不会进行任何检查以查看是否已添加监听器。多次调用传递相同组合的事件和监听器将导致监听器被多次添加。返回发射器,因此可以链接调用。 |
3 | once(event, listener) 向事件添加一次性监听器。此监听器仅在下一次触发事件时调用,之后将其删除。返回发射器,因此可以链接调用。 |
4 |
removeListener(event, listener) 从指定事件的监听器数组中移除一个监听器。注意 − 它会更改监听器数组中监听器后面的数组索引。removeListener 最多只会从监听器数组中移除一个监听器实例。如果任何单个监听器已多次添加到指定事件的监听器数组中,则必须多次调用 removeListener 才能移除每个实例。返回 emitter,以便可以链接调用。 |
5 |
removeAllListeners([event]) 移除所有监听器,或指定事件的监听器。移除在代码其他地方添加的监听器不是一个好主意,尤其是在您没有创建的 emitter 上(例如套接字或文件流)。返回 emitter,以便可以链接调用。 |
6 |
setMaxListeners(n) 默认情况下,如果为特定事件添加了超过 10 个监听器,EventEmitters 将打印警告。这是一个有用的默认设置,有助于查找内存泄漏。显然,并非所有 Emitters 都应该限制为 10 个。此函数允许增加此限制。设置为零表示无限制。 |
7 |
listeners(event) 返回指定事件的监听器数组。 |
8 |
emit(event, [arg1], [arg2], [...]) 按顺序使用提供的参数执行每个监听器。如果事件有监听器,则返回 true,否则返回 false。 |
类方法
序号 | 方法和描述 |
---|---|
1 |
listenerCount(emitter, event) 返回给定事件的监听器数量。 |
事件
序号 | 事件与描述 |
---|---|
1 | newListener
每当添加监听器时都会发出此事件。触发此事件时,监听器可能尚未添加到该事件的监听器数组中。 |
2 |
removeListener
每当有人移除监听器时都会发出此事件。触发此事件时,监听器可能尚未从该事件的监听器数组中移除。 |
示例
创建一个名为 main.js 的 js 文件,其中包含以下 Node.js 代码 −
var events = require('events'); var eventEmitter = new events.EventEmitter(); // listener #1 var listner1 = function listner1() { console.log('listner1 executed.'); } // listener #2 var listner2 = function listner2() { console.log('listner2 executed.'); } // Bind the connection event with the listner1 function eventEmitter.addListener('connection', listner1); // Bind the connection event with the listner2 function eventEmitter.on('connection', listner2); var eventListeners = require('events').EventEmitter.listenerCount (eventEmitter,'connection'); console.log(eventListeners + " Listner(s) listening to connection event"); // Fire the connection event eventEmitter.emit('connection'); // Remove the binding of listner1 function eventEmitter.removeListener('connection', listner1); console.log("Listner1 will not listen now."); // Fire the connection event eventEmitter.emit('connection'); eventListeners = require('events').EventEmitter.listenerCount(eventEmitter,'connection'); console.log(eventListeners + " Listner(s) listening to connection event"); console.log("Program Ended.");
现在运行main.js查看结果 −
$ node main.js
验证输出。
2 Listner(s) listening to connection event listner1 executed. listner2 executed. Listner1 will not listen now. listner2 executed. 1 Listner(s) listening to connection event Program Ended.
Node.js - 缓冲区
纯 JavaScript 支持 Unicode,但不支持二进制数据。在处理 TCP 流或文件系统时,需要处理八位字节流。Node 提供 Buffer 类,该类提供实例来存储原始数据,类似于整数数组,但对应于 V8 堆之外的原始内存分配。
Buffer 类是一个全局类,无需导入 buffer 模块即可在应用程序中访问。
创建缓冲区
Node Buffer 可以通过多种方式构建。
方法 1
以下是创建10个八位字节的未初始化缓冲区的语法 −
var buf = new Buffer(10);
方法 2
以下是根据给定数组创建缓冲区的语法 −
var buf = new Buffer([10, 20, 30, 40, 50]);
方法 3
以下是根据给定字符串和可选编码类型创建缓冲区的语法 −
var buf = new Buffer("Simply Easy Learning", "utf-8");
虽然“utf8”是默认编码,但您可以使用以下任何编码:“ascii”、“utf8”、“utf16le”、“ucs2”、“base64”或“hex”。
写入缓冲区
语法
以下是写入 Node Buffer 的方法的语法 −
buf.write(string[, offset][, length][, encoding])
参数
以下是所用参数的描述 −
字符串 − 要写入缓冲区的字符串数据。
偏移量 − 开始写入的缓冲区索引。默认值为 0。
长度 − 要写入的字节数。默认为 buffer.length。
编码 − 要使用的编码。'utf8' 是默认编码。
返回值
此方法返回写入的八位字节数。如果缓冲区中没有足够的空间容纳整个字符串,它将写入字符串的一部分。
示例
buf = new Buffer(256); len = buf.write("Simply Easy Learning"); console.log("Octets written : "+ len);
执行上述程序后,将产生以下结果 −
Octets written : 20
从缓冲区读取
语法
以下是用于从 Node Buffer 读取数据的方法的语法 −
buf.toString([encoding][, start][, end])
参数
以下是所用参数的描述 −
编码 − 要使用的编码。'utf8' 是默认编码。
开始 − 开始读取的起始索引,默认为 0。
结束 − 结束读取的结束索引,默认为整个缓冲区。
返回值
此方法使用指定的字符集编码解码并返回缓冲区数据中的字符串。
示例
buf = new Buffer(26); for (var i = 0 ; i < 26 ; i++) { buf[i] = i + 97; } console.log( buf.toString('ascii')); // outputs: abcdefghijklmnopqrstuvwxyz console.log( buf.toString('ascii',0,5)); // outputs: abcde console.log( buf.toString('utf8',0,5)); // outputs: abcde console.log( buf.toString(undefined,0,5)); // encoding defaults to 'utf8', outputs abcde
执行上述程序后,将产生以下结果 −
abcdefghijklmnopqrstuvwxyz abcde abcde abcde
将缓冲区转换为 JSON
语法
以下是将 Node Buffer 转换为 JSON 对象的方法的语法 −
buf.toJSON()
返回值
此方法返回 Buffer 实例的 JSON 表示形式。
示例
var buf = new Buffer('Simply Easy Learning'); var json = buf.toJSON(buf); console.log(json);
执行上述程序后,将产生以下结果 −
{ type: 'Buffer', data: [ 83, 105, 109, 112, 108, 121, 32, 69, 97, 115, 121, 32, 76, 101, 97, 114, 110, 105, 110, 103 ] }
连接缓冲区
语法
以下是将 Node 缓冲区连接到单个 Node Buffer 的方法的语法 −
Buffer.concat(list[, totalLength])
参数
以下是所用参数的描述 −
列表 − 要连接的 Buffer 对象的数组列表。
总长度 − 连接后缓冲区的总长度。
返回值
此方法返回一个 Buffer 实例。
示例
var buffer1 = new Buffer('TutorialsPoint '); var buffer2 = new Buffer('Simply Easy Learning'); var buffer3 = Buffer.concat([buffer1,buffer2]); console.log("buffer3 content: " + buffer3.toString());
执行上述程序后,将产生以下结果 −
buffer3 content: TutorialsPoint Simply Easy Learning
比较缓冲区
语法
以下是比较两个 Node 缓冲区的方法的语法 −
buf.compare(otherBuffer);
参数
以下是所用参数的描述 −
otherBuffer − 将与buf比较的另一个缓冲区
返回值
返回一个数字,指示它在排序顺序中是在 otherBuffer 之前、之后还是与之相同。
示例
var buffer1 = new Buffer('ABC'); var buffer2 = new Buffer('ABCD'); var result = buffer1.compare(buffer2); if(result < 0) { console.log(buffer1 +" comes before " + buffer2); } else if(result === 0) { console.log(buffer1 +" is same as " + buffer2); } else { console.log(buffer1 +" comes after " + buffer2); }
执行上述程序后,将产生以下结果 −
ABC comes before ABCD
复制缓冲区
语法
以下是复制节点缓冲区的方法的语法 −
buf.copy(targetBuffer[, targetStart][, sourceStart][, sourceEnd])
参数
以下是所用参数的描述 −
targetBuffer − 将复制缓冲区的 Buffer 对象。
targetStart − 数字,可选,默认值:0
sourceStart − 数字,可选,默认值:0
sourceEnd − 数字,可选,默认值:buffer.length
返回值
无返回值。即使目标内存区域与源内存区域重叠,也会将数据从此缓冲区的某个区域复制到目标缓冲区的某个区域。如果未定义,则 targetStart 和 sourceStart 参数默认为 0,而 sourceEnd 默认为 buffer.length。
示例
var buffer1 = new Buffer('ABC'); //copy a buffer var buffer2 = new Buffer(3); buffer1.copy(buffer2); console.log("buffer2 content: " + buffer2.toString());
执行上述程序后,将产生以下结果 −
buffer2 content: ABC
切片缓冲区
语法
以下是获取节点缓冲区的子缓冲区的方法的语法 −
buf.slice([start][, end])
参数
以下是所用参数的描述 −
开始 − 数字,可选,默认值:0
结束 − 数字,可选,默认值:buffer.length
返回值
返回一个新的缓冲区,它引用与旧缓冲区相同的内存,但通过起始(默认为 0)和结束(默认为 buffer.length)索引进行偏移和裁剪。负索引从缓冲区的末尾开始。
示例
var buffer1 = new Buffer('TutorialsPoint'); //slicing a buffer var buffer2 = buffer1.slice(0,9); console.log("buffer2 content: " + buffer2.toString());
执行上述程序后,将产生以下结果 −
buffer2 content: Tutorials
缓冲区长度
语法
以下是获取节点缓冲区大小(以字节为单位)的方法的语法 −
buf.length;
返回值
返回缓冲区的大小(以字节为单位)。
示例
var buffer = new Buffer('TutorialsPoint'); //length of the buffer console.log("buffer length: " + buffer.length);
执行上述程序后,将产生以下结果 −
buffer length: 14
方法参考
类方法
序号 | 方法和描述 |
---|---|
1 |
Buffer.isEncoding(encoding) 如果编码是一个有效的编码参数,则返回true,否则返回false。 |
2 |
Buffer.isBuffer(obj) 测试obj是否是一个Buffer。 |
3 |
Buffer.byteLength(string[, encoding]) 给出字符串的实际字节长度。encoding默认为'utf8'。它与String.prototype.length不同,因为String.prototype.length返回字符串中字符的数量。 |
4 |
Buffer.concat(list[, totalLength]) 返回一个缓冲区,它是将列表中所有缓冲区连接在一起的结果。 |
5 |
Buffer.compare(buf1, buf2) 与buf1.compare(buf2)相同。对于排序缓冲区数组很有用。 |
Node.js - 流
什么是流?
流是允许您以连续方式从源读取数据或向目标写入数据的对象。在Node.js中,有四种类型的流:
可读流 - 用于读取操作的流。
可写流 - 用于写入操作的流。
双工流 - 可用于读取和写入操作的流。
转换流 - 一种双工流,其中输出是基于输入计算的。
每种类型的流都是一个EventEmitter实例,并在不同的时间实例中抛出多个事件。例如,一些常用的事件是:
data - 当有数据可供读取时触发此事件。
end - 当没有更多数据可读取时触发此事件。
error - 当接收或写入数据时发生任何错误时触发此事件。
finish - 当所有数据都已刷新到底层系统时触发此事件。
本教程提供了对流上常用操作的基本理解。
从流中读取
创建一个名为input.txt的文本文件,内容如下:
Tutorials Point is giving self learning content to teach the world in simple and easy way!!!!!
创建一个名为main.js的js文件,代码如下 −
var fs = require("fs"); var data = ''; // Create a readable stream var readerStream = fs.createReadStream('input.txt'); // Set the encoding to be utf8. readerStream.setEncoding('UTF8'); // Handle stream events --> data, end, and error readerStream.on('data', function(chunk) { data += chunk; }); readerStream.on('end',function() { console.log(data); }); readerStream.on('error', function(err) { console.log(err.stack); }); console.log("Program Ended");
现在运行main.js查看结果 −
$ node main.js
验证输出。
Program Ended Tutorials Point is giving self learning content to teach the world in simple and easy way!!!!!
写入流
创建一个名为main.js的js文件,代码如下 −
var fs = require("fs"); var data = 'Simply Easy Learning'; // Create a writable stream var writerStream = fs.createWriteStream('output.txt'); // Write the data to stream with encoding to be utf8 writerStream.write(data,'UTF8'); // Mark the end of file writerStream.end(); // Handle stream events --> finish, and error writerStream.on('finish', function() { console.log("Write completed."); }); writerStream.on('error', function(err) { console.log(err.stack); }); console.log("Program Ended");
现在运行main.js查看结果 −
$ node main.js
验证输出。
Program Ended Write completed.
现在打开您当前目录中创建的output.txt;它应该包含以下内容:
Simply Easy Learning
管道流
管道是一种机制,我们将一个流的输出作为另一个流的输入。它通常用于从一个流获取数据并将该流的输出传递到另一个流。管道操作没有限制。现在我们将展示一个从一个文件读取并将其写入另一个文件的管道示例。
创建一个名为main.js的js文件,代码如下 −
var fs = require("fs"); // Create a readable stream var readerStream = fs.createReadStream('input.txt'); // Create a writable stream var writerStream = fs.createWriteStream('output.txt'); // Pipe the read and write operations // read input.txt and write data to output.txt readerStream.pipe(writerStream); console.log("Program Ended");
现在运行main.js查看结果 −
$ node main.js
验证输出。
Program Ended
现在打开您当前目录中创建的output.txt;它应该包含以下内容:
Tutorials Point is giving self learning content to teach the world in simple and easy way!!!!!
流的链式操作
链式操作是一种将一个流的输出连接到另一个流并创建多个流操作链的机制。它通常与管道操作一起使用。现在我们将使用管道和链式操作先压缩一个文件,然后再解压缩同一个文件。
创建一个名为main.js的js文件,代码如下 −
var fs = require("fs"); var zlib = require('zlib'); // Compress the file input.txt to input.txt.gz fs.createReadStream('input.txt') .pipe(zlib.createGzip()) .pipe(fs.createWriteStream('input.txt.gz')); console.log("File Compressed.");
现在运行main.js查看结果 −
$ node main.js
验证输出。
File Compressed.
您会发现input.txt已被压缩,并在当前目录中创建了一个名为input.txt.gz的文件。现在让我们尝试使用以下代码解压缩同一个文件:
var fs = require("fs"); var zlib = require('zlib'); // Decompress the file input.txt.gz to input.txt fs.createReadStream('input.txt.gz') .pipe(zlib.createGunzip()) .pipe(fs.createWriteStream('input.txt')); console.log("File Decompressed.");
现在运行main.js查看结果 −
$ node main.js
验证输出。
File Decompressed.
Node.js - 文件系统
Node使用围绕标准POSIX函数的简单包装器实现文件I/O。Node文件系统(fs)模块可以使用以下语法导入:
var fs = require("fs")
同步与异步
fs 模块中的每个方法都具有同步和异步形式。异步方法将最后一个参数作为完成函数回调,回调函数的第一个参数作为错误。最好使用异步方法而不是同步方法,因为前者在执行期间不会阻塞程序,而后者会。
示例
创建一个名为input.txt的文本文件,内容如下 −
Tutorials Point is giving self learning content to teach the world in simple and easy way!!!!!
让我们创建一个名为main.js的 js 文件,其中包含以下代码:
var fs = require("fs"); // Asynchronous read fs.readFile('input.txt', function (err, data) { if (err) { return console.error(err); } console.log("Asynchronous read: " + data.toString()); }); // Synchronous read var data = fs.readFileSync('input.txt'); console.log("Synchronous read: " + data.toString()); console.log("Program Ended");
现在运行main.js查看结果 −
$ node main.js
验证输出。
Synchronous read: Tutorials Point is giving self learning content to teach the world in simple and easy way!!!!! Program Ended Asynchronous read: Tutorials Point is giving self learning content to teach the world in simple and easy way!!!!!
本章的以下部分提供了一组关于主要文件 I/O 方法的良好示例。
打开文件
语法
以下是异步模式下打开文件的语法:
fs.open(path, flags[, mode], callback)
参数
以下是所用参数的描述 −
path - 这是一个包含路径的文件名字符串。
flags - 标志指示要打开的文件的行为。所有可能的值如下所示。
mode - 它设置文件模式(权限和粘滞位),但仅当文件被创建时才设置。默认为 0666,可读写。
callback - 这是一个回调函数,它获取两个参数 (err, fd)。
标志
读/写操作的标志:
序号 | 标志 & 描述 |
---|---|
1 |
r 打开文件以进行读取。如果文件不存在,则会发生异常。 |
2 |
r+ 打开文件以进行读写。如果文件不存在,则会发生异常。 |
3 |
rs 以同步模式打开文件进行读取。 |
4 |
rs+ 打开文件进行读写,要求操作系统以同步方式打开它。关于谨慎使用此选项与 'rs' 的说明,请参见注释。 |
5 |
w 打开文件以进行写入。如果文件不存在,则创建文件;如果文件存在,则截断文件。 |
6 |
wx 类似于 'w',但如果路径存在则失败。 |
7 |
w+ 打开文件以进行读写。如果文件不存在,则创建文件;如果文件存在,则截断文件。 |
8 |
wx+ 类似于 'w+',但如果路径存在则失败。 |
9 |
a 打开文件以进行追加。如果文件不存在,则创建文件。 |
10 |
ax 类似于 'a',但如果路径存在则失败。 |
11 |
a+ 打开文件以进行读取和追加。如果文件不存在,则创建文件。 |
12 |
ax+ 类似于 'a+',但如果路径存在则失败。 |
示例
让我们创建一个名为main.js的 js 文件,其中包含以下代码以打开 input.txt 文件进行读写。
var fs = require("fs"); // Asynchronous - Opening File console.log("Going to open file!"); fs.open('input.txt', 'r+', function(err, fd) { if (err) { return console.error(err); } console.log("File opened successfully!"); });
现在运行main.js查看结果 −
$ node main.js
验证输出。
Going to open file! File opened successfully!
获取文件信息
语法
以下是获取有关文件信息的方法的语法:
fs.stat(path, callback)
参数
以下是所用参数的描述 −
path - 这是一个包含路径的文件名字符串。
callback - 这是一个回调函数,它获取两个参数 (err, stats),其中stats 是 fs.Stats 类型的对象,示例中打印如下。
除了示例中打印的重要属性之外,fs.Stats 类中还提供了一些有用的方法,可用于检查文件类型。这些方法在下面的表格中给出。
序号 | 方法和描述 |
---|---|
1 |
stats.isFile() 如果文件类型是普通文件,则返回 true。 |
2 |
stats.isDirectory() 如果文件类型是目录,则返回 true。 |
3 |
stats.isBlockDevice() 如果文件类型是块设备,则返回 true。 |
4 |
stats.isCharacterDevice() 如果文件类型是字符设备,则返回 true。 |
5 |
stats.isSymbolicLink() 如果文件类型是符号链接,则返回 true。 |
6 |
stats.isFIFO() 如果文件类型是 FIFO,则返回 true。 |
7 |
stats.isSocket() 如果文件类型是套接字,则返回 true。 |
示例
让我们创建一个名为main.js的 js 文件,其中包含以下代码:
var fs = require("fs"); console.log("Going to get file info!"); fs.stat('input.txt', function (err, stats) { if (err) { return console.error(err); } console.log(stats); console.log("Got file info successfully!"); // Check file type console.log("isFile ? " + stats.isFile()); console.log("isDirectory ? " + stats.isDirectory()); });
现在运行main.js查看结果 −
$ node main.js
验证输出。
Going to get file info! { dev: 1792, mode: 33188, nlink: 1, uid: 48, gid: 48, rdev: 0, blksize: 4096, ino: 4318127, size: 97, blocks: 8, atime: Sun Mar 22 2015 13:40:00 GMT-0500 (CDT), mtime: Sun Mar 22 2015 13:40:57 GMT-0500 (CDT), ctime: Sun Mar 22 2015 13:40:57 GMT-0500 (CDT) } Got file info successfully! isFile ? true isDirectory ? false
写入文件
语法
以下是写入文件的方法之一的语法:
fs.writeFile(filename, data[, options], callback)
如果文件已存在,此方法将覆盖该文件。如果要写入现有文件,则应使用另一种可用方法。
参数
以下是所用参数的描述 −
path - 这是一个包含路径的文件名字符串。
data - 这是要写入文件的字符串或缓冲区。
options - 第三个参数是一个对象,它将包含 {encoding, mode, flag}。默认情况下,encoding 为 utf8,mode 为八进制值 0666,flag 为 'w'。
callback - 这是一个回调函数,它获取一个参数 err,该参数在出现任何写入错误时返回错误。
示例
让我们创建一个名为main.js的 js 文件,其中包含以下代码:
var fs = require("fs"); console.log("Going to write into existing file"); fs.writeFile('input.txt', 'Simply Easy Learning!', function(err) { if (err) { return console.error(err); } console.log("Data written successfully!"); console.log("Let's read newly written data"); fs.readFile('input.txt', function (err, data) { if (err) { return console.error(err); } console.log("Asynchronous read: " + data.toString()); }); });
现在运行main.js查看结果 −
$ node main.js
验证输出。
Going to write into existing file Data written successfully! Let's read newly written data Asynchronous read: Simply Easy Learning!
读取文件
语法
以下是读取文件的方法之一的语法:
fs.read(fd, buffer, offset, length, position, callback)
此方法将使用文件描述符读取文件。如果要直接使用文件名读取文件,则应使用另一种可用方法。
参数
以下是所用参数的描述 −
fd - 这是 fs.open() 返回的文件描述符。
buffer - 这是将数据写入到的缓冲区。
offset - 这是在缓冲区中开始写入的偏移量。
length - 这是一个整数,指定要读取的字节数。
position - 这是一个整数,指定从文件的何处开始读取。如果 position 为 null,则将从当前文件位置读取数据。
callback - 这是一个回调函数,它获取三个参数:(err, bytesRead, buffer)。
示例
让我们创建一个名为main.js的 js 文件,其中包含以下代码:
var fs = require("fs"); var buf = new Buffer(1024); console.log("Going to open an existing file"); fs.open('input.txt', 'r+', function(err, fd) { if (err) { return console.error(err); } console.log("File opened successfully!"); console.log("Going to read the file"); fs.read(fd, buf, 0, buf.length, 0, function(err, bytes){ if (err){ console.log(err); } console.log(bytes + " bytes read"); // Print only read bytes to avoid junk. if(bytes > 0){ console.log(buf.slice(0, bytes).toString()); } }); });
现在运行main.js查看结果 −
$ node main.js
验证输出。
Going to open an existing file File opened successfully! Going to read the file 97 bytes read Tutorials Point is giving self learning content to teach the world in simple and easy way!!!!!
关闭文件
语法
以下是关闭已打开文件的语法:
fs.close(fd, callback)
参数
以下是所用参数的描述 −
fd - 这是文件 fs.open() 方法返回的文件描述符。
callback - 这是一个回调函数,除了可能的异常之外,没有其他参数传递给完成回调。
示例
让我们创建一个名为main.js的 js 文件,其中包含以下代码:
var fs = require("fs"); var buf = new Buffer(1024); console.log("Going to open an existing file"); fs.open('input.txt', 'r+', function(err, fd) { if (err) { return console.error(err); } console.log("File opened successfully!"); console.log("Going to read the file"); fs.read(fd, buf, 0, buf.length, 0, function(err, bytes) { if (err) { console.log(err); } // Print only read bytes to avoid junk. if(bytes > 0) { console.log(buf.slice(0, bytes).toString()); } // Close the opened file. fs.close(fd, function(err) { if (err) { console.log(err); } console.log("File closed successfully."); }); }); });
现在运行main.js查看结果 −
$ node main.js
验证输出。
Going to open an existing file File opened successfully! Going to read the file Tutorials Point is giving self learning content to teach the world in simple and easy way!!!!! File closed successfully.
截断文件
语法
以下是截断已打开文件的方法的语法:
fs.ftruncate(fd, len, callback)
参数
以下是所用参数的描述 −
fd - 这是 fs.open() 返回的文件描述符。
len - 这是文件截断后的长度。
callback - 这是一个回调函数,除了可能的异常之外,没有其他参数传递给完成回调。
示例
让我们创建一个名为main.js的 js 文件,其中包含以下代码:
var fs = require("fs"); var buf = new Buffer(1024); console.log("Going to open an existing file"); fs.open('input.txt', 'r+', function(err, fd) { if (err) { return console.error(err); } console.log("File opened successfully!"); console.log("Going to truncate the file after 10 bytes"); // Truncate the opened file. fs.ftruncate(fd, 10, function(err) { if (err) { console.log(err); } console.log("File truncated successfully."); console.log("Going to read the same file"); fs.read(fd, buf, 0, buf.length, 0, function(err, bytes){ if (err) { console.log(err); } // Print only read bytes to avoid junk. if(bytes > 0) { console.log(buf.slice(0, bytes).toString()); } // Close the opened file. fs.close(fd, function(err) { if (err) { console.log(err); } console.log("File closed successfully."); }); }); }); });
现在运行main.js查看结果 −
$ node main.js
验证输出。
Going to open an existing file File opened successfully! Going to truncate the file after 10 bytes File truncated successfully. Going to read the same file Tutorials File closed successfully.
删除文件
语法
以下是删除文件的方法的语法:
fs.unlink(path, callback)
参数
以下是所用参数的描述 −
path - 这是一个包含路径的文件名。
callback - 这是一个回调函数,除了可能的异常之外,没有其他参数传递给完成回调。
示例
让我们创建一个名为main.js的 js 文件,其中包含以下代码:
var fs = require("fs"); console.log("Going to delete an existing file"); fs.unlink('input.txt', function(err) { if (err) { return console.error(err); } console.log("File deleted successfully!"); });
现在运行main.js查看结果 −
$ node main.js
验证输出。
Going to delete an existing file File deleted successfully!
创建目录
语法
以下是创建目录的方法的语法:
fs.mkdir(path[, mode], callback)
参数
以下是所用参数的描述 −
path - 这是一个包含路径的目录名。
mode - 这是要设置的目录权限。默认为 0777。
callback - 这是一个回调函数,除了可能的异常之外,没有其他参数传递给完成回调。
示例
让我们创建一个名为main.js的 js 文件,其中包含以下代码:
var fs = require("fs"); console.log("Going to create directory /tmp/test"); fs.mkdir('/tmp/test',function(err) { if (err) { return console.error(err); } console.log("Directory created successfully!"); });
现在运行main.js查看结果 −
$ node main.js
验证输出。
Going to create directory /tmp/test Directory created successfully!
读取目录
语法
以下是读取目录的方法的语法:
fs.readdir(path, callback)
参数
以下是所用参数的描述 −
path - 这是一个包含路径的目录名。
callback - 这是一个回调函数,它获取两个参数 (err, files),其中 files 是目录中文件名的数组,不包括 '.' 和 '..'。
示例
让我们创建一个名为main.js的 js 文件,其中包含以下代码:
var fs = require("fs"); console.log("Going to read directory /tmp"); fs.readdir("/tmp/",function(err, files) { if (err) { return console.error(err); } files.forEach( function (file) { console.log( file ); }); });
现在运行main.js查看结果 −
$ node main.js
验证输出。
Going to read directory /tmp ccmzx99o.out ccyCSbkF.out employee.ser hsperfdata_apache test test.txt
删除目录
语法
以下是删除目录的方法的语法:
fs.rmdir(path, callback)
参数
以下是所用参数的描述 −
path - 这是一个包含路径的目录名。
callback - 这是一个回调函数,除了可能的异常之外,没有其他参数传递给完成回调。
示例
让我们创建一个名为main.js的 js 文件,其中包含以下代码:
var fs = require("fs"); console.log("Going to delete directory /tmp/test"); fs.rmdir("/tmp/test",function(err) { if (err) { return console.error(err); } console.log("Going to read directory /tmp"); fs.readdir("/tmp/",function(err, files) { if (err) { return console.error(err); } files.forEach( function (file) { console.log( file ); }); }); });
现在运行main.js查看结果 −
$ node main.js
验证输出。
Going to read directory /tmp ccmzx99o.out ccyCSbkF.out employee.ser hsperfdata_apache test.txt
方法参考
Node.js - 全局对象
Node.js 全局对象本质上是全局的,它们在所有模块中都可用。我们不需要在应用程序中包含这些对象,而可以直接使用它们。这些对象是模块、函数、字符串和对象本身,如下所述。
__filename
__filename 代表正在执行的代码的文件名。这是此代码文件的已解析绝对路径。对于主程序,这不一定与命令行中使用的文件名相同。模块内部的值是该模块文件的路径。
示例
创建一个名为main.js的js文件,代码如下 −
// Let's try to print the value of __filename console.log( __filename );
现在运行main.js查看结果 −
$ node main.js
根据程序的位置,它将打印主文件名如下:
/web/com/1427091028_21099/main.js
__dirname
__dirname 代表当前正在执行的脚本所在的目录的名称。
示例
创建一个名为main.js的js文件,代码如下 −
// Let's try to print the value of __dirname console.log( __dirname );
现在运行main.js查看结果 −
$ node main.js
根据程序的位置,它将打印当前目录名称如下:
/web/com/1427091028_21099
setTimeout(cb, ms)
setTimeout(cb, ms) 全局函数用于在至少 ms 毫秒后运行回调 cb。实际延迟取决于外部因素,例如操作系统计时器粒度和系统负载。计时器不能超过 24.8 天。
此函数返回一个不透明值,该值表示可用于清除计时器的计时器。
示例
创建一个名为main.js的js文件,代码如下 −
function printHello() { console.log( "Hello, World!"); } // Now call above function after 2 seconds setTimeout(printHello, 2000);
现在运行main.js查看结果 −
$ node main.js
验证输出是否在稍后延迟后打印。
Hello, World!
clearTimeout(t)
clearTimeout(t) 全局函数用于停止之前使用 setTimeout() 创建的计时器。这里 t 是 setTimeout() 函数返回的计时器。
示例
创建一个名为main.js的js文件,代码如下 −
function printHello() { console.log( "Hello, World!"); } // Now call above function after 2 seconds var t = setTimeout(printHello, 2000); // Now clear the timer clearTimeout(t);
现在运行main.js查看结果 −
$ node main.js
验证输出,您将找不到任何打印的内容。
setInterval(cb, ms)
setInterval(cb, ms) 全局函数用于在至少 ms 毫秒后重复运行回调 cb。实际延迟取决于外部因素,例如操作系统计时器粒度和系统负载。计时器不能超过 24.8 天。
此函数返回一个不透明值,该值表示可使用函数 clearInterval(t) 清除计时器的计时器。
示例
创建一个名为main.js的js文件,代码如下 −
function printHello() { console.log( "Hello, World!"); } // Now call above function after 2 seconds setInterval(printHello, 2000);
现在运行main.js查看结果 −
$ node main.js
上述程序将在每 2 秒后执行 printHello()。由于系统限制。
全局对象
下表列出了我们在应用程序中经常使用的其他对象。有关更多详细信息,您可以参考官方文档。
Node.js - 实用工具模块
Node.js 模块库中提供了一些实用程序模块。这些模块非常常见,在开发任何基于 Node 的应用程序时经常使用。
序号 | 模块名称和描述 |
---|---|
1 | OS 模块
提供基本的与操作系统相关的实用程序函数。 |
2 | Path 模块
提供用于处理和转换文件路径的实用程序。 |
3 | Net 模块
提供服务器和客户端作为流。充当网络包装器。 |
4 | DNS 模块
提供执行实际 DNS 查找以及使用底层操作系统名称解析功能的函数。 |
5 | Domain 模块
提供将多个不同的 I/O 操作作为单个组处理的方法。 |
Node.js - Web 模块
什么是 Web 服务器?
Web 服务器是一个软件应用程序,它处理 HTTP 客户端(如 Web 浏览器)发送的 HTTP 请求,并响应客户端的请求返回网页。Web 服务器通常提供 html 文档以及图像、样式表和脚本。
大多数 Web 服务器都支持服务器端脚本,使用脚本语言或将任务重定向到应用程序服务器,该服务器从数据库检索数据并执行复杂的逻辑,然后通过 Web 服务器将结果发送到 HTTP 客户端。
Apache Web 服务器是最常用的 Web 服务器之一。它是一个开源项目。
Web 应用程序架构
Web 应用程序通常分为四个层:
客户端 - 此层包含 Web 浏览器、移动浏览器或可以向 Web 服务器发出 HTTP 请求的应用程序。
服务器 - 此层包含可以拦截客户端发出的请求并将响应传递给它们的 Web 服务器。
业务 - 此层包含 Web 服务器用来执行所需处理的应用程序服务器。此层通过数据库或某些外部程序与数据层交互。
数据 - 此层包含数据库或任何其他数据源。
使用 Node 创建 Web 服务器
Node.js 提供了一个 http 模块,可用于创建服务器的 HTTP 客户端。以下是侦听 8081 端口的 HTTP 服务器的最小结构。
创建一个名为 server.js 的 js 文件:
文件:server.js
var http = require('http'); var fs = require('fs'); var url = require('url'); // Create a server http.createServer( function (request, response) { // Parse the request containing file name var pathname = url.parse(request.url).pathname; // Print the name of the file for which request is made. console.log("Request for " + pathname + " received."); // Read the requested file content from file system fs.readFile(pathname.substr(1), function (err, data) { if (err) { console.log(err); // HTTP Status: 404 : NOT FOUND // Content Type: text/plain response.writeHead(404, {'Content-Type': 'text/html'}); } else { //Page found // HTTP Status: 200 : OK // Content Type: text/plain response.writeHead(200, {'Content-Type': 'text/html'}); // Write the content of the file to response body response.write(data.toString()); } // Send the response body response.end(); }); }).listen(8081); // Console will print the message console.log('Server running at http://127.0.0.1:8081/');
接下来,让我们在创建 server.js 的同一目录中创建名为 index.htm 的以下 html 文件。
文件:index.htm
<html> <head> <title>Sample Page</title> </head> <body> Hello World! </body> </html>
现在让我们运行 server.js 以查看结果:
$ node server.js
验证输出。
Server running at http://127.0.0.1:8081/
向 Node.js 服务器发出请求
在任何浏览器中打开 http://127.0.0.1:8081/index.htm 以查看以下结果。
验证服务器端的输出。
Server running at http://127.0.0.1:8081/ Request for /index.htm received.
使用 Node 创建 Web 客户端
可以使用 http 模块创建 Web 客户端。让我们检查以下示例。
创建一个名为 client.js 的 js 文件:
文件:client.js
var http = require('http'); // Options to be used by request var options = { host: 'localhost', port: '8081', path: '/index.htm' }; // Callback function is used to deal with response var callback = function(response) { // Continuously update stream with data var body = ''; response.on('data', function(data) { body += data; }); response.on('end', function() { // Data received completely. console.log(body); }); } // Make a request to the server var req = http.request(options, callback); req.end();
现在从与 server.js 不同的命令终端运行 client.js 以查看结果:
$ node client.js
验证输出。
<html> <head> <title>Sample Page</title> </head> <body> Hello World! </body> </html>
验证服务器端的输出。
Server running at http://127.0.0.1:8081/ Request for /index.htm received.
Node.js - Express 框架
Express 概述
Express 是一个最小且灵活的 Node.js Web 应用程序框架,它提供了一套强大的功能来开发 Web 和移动应用程序。它有助于快速开发基于 Node 的 Web 应用程序。以下是 Express 框架的一些核心功能:
允许设置中间件以响应 HTTP 请求。
定义路由表,用于根据 HTTP 方法和 URL 执行不同的操作。
允许根据向模板传递参数动态渲染 HTML 页面。
安装 Express
首先,使用 NPM 全局安装 Express 框架,以便可以使用 node 终端来创建 Web 应用程序。
$ npm install express --save
上述命令将安装保存在 node_modules 目录中,并在 node_modules 内创建 express 目录。您应该与 express 一起安装以下重要模块:
body-parser - 这是一个用于处理 JSON、Raw、Text 和 URL 编码表单数据的 node.js 中间件。
cookie-parser - 解析 Cookie 标头并将 req.cookies 填充为一个以 Cookie 名称为键的对象。
multer - 这是一个用于处理 multipart/form-data 的 node.js 中间件。
$ npm install body-parser --save $ npm install cookie-parser --save $ npm install multer --save
Hello world 示例
以下是一个非常基本的 Express 应用程序,它启动一个服务器并在 8081 端口上侦听连接。此应用程序对主页的请求返回 Hello World!。对于其他所有路径,它将返回 404 Not Found。
var express = require('express'); var app = express(); app.get('/', function (req, res) { res.send('Hello World'); }) var server = app.listen(8081, function () { var host = server.address().address var port = server.address().port console.log("Example app listening at http://%s:%s", host, port) })
将以上代码保存到名为 server.js 的文件中,并使用以下命令运行它。
$ node server.js
您将看到以下输出:
Example app listening at http://0.0.0.0:8081
在任何浏览器中打开 http://127.0.0.1:8081/ 以查看以下结果。
请求和响应
Express 应用程序使用回调函数,其参数是 request 和 response 对象。
app.get('/', function (req, res) { // -- })
请求对象 - 请求对象表示 HTTP 请求,并具有请求查询字符串、参数、主体、HTTP 标头等的属性。
响应对象 - 响应对象表示 Express 应用程序在收到 HTTP 请求时发送的 HTTP 响应。
您可以打印 req 和 res 对象,它们提供了许多与 HTTP 请求和响应相关的信息,包括 Cookie、会话、URL 等。
基本路由
我们已经看到了一个为主页提供 HTTP 请求的基本应用程序。路由是指确定应用程序如何响应客户端对特定端点的请求,端点是 URI(或路径)和特定的 HTTP 请求方法(GET、POST 等)。
我们将扩展我们的“Hello World”程序,使其能够处理更多类型的HTTP请求。
var express = require('express'); var app = express(); // This responds with "Hello World" on the homepage app.get('/', function (req, res) { console.log("Got a GET request for the homepage"); res.send('Hello GET'); }) // This responds a POST request for the homepage app.post('/', function (req, res) { console.log("Got a POST request for the homepage"); res.send('Hello POST'); }) // This responds a DELETE request for the /del_user page. app.delete('/del_user', function (req, res) { console.log("Got a DELETE request for /del_user"); res.send('Hello DELETE'); }) // This responds a GET request for the /list_user page. app.get('/list_user', function (req, res) { console.log("Got a GET request for /list_user"); res.send('Page Listing'); }) // This responds a GET request for abcd, abxcd, ab123cd, and so on app.get('/ab*cd', function(req, res) { console.log("Got a GET request for /ab*cd"); res.send('Page Pattern Match'); }) var server = app.listen(8081, function () { var host = server.address().address var port = server.address().port console.log("Example app listening at http://%s:%s", host, port) })
将以上代码保存到名为 server.js 的文件中,并使用以下命令运行它。
$ node server.js
您将看到以下输出:
Example app listening at http://0.0.0.0:8081
现在您可以尝试不同的请求,访问 http://127.0.0.1:8081 来查看 server.js 生成的输出。以下是一些屏幕截图,显示了不同 URL 的不同响应。
显示 http://127.0.0.1:8081/list_user 的屏幕截图
显示 http://127.0.0.1:8081/abcd 的屏幕截图
显示 http://127.0.0.1:8081/abcdefg 的屏幕截图
服务静态文件
Express 提供了一个内置中间件 **express.static** 来服务静态文件,例如图像、CSS、JavaScript 等。
您只需将保存静态资源的目录名称传递给 **express.static** 中间件,即可开始直接服务这些文件。例如,如果您将图像、CSS 和 JavaScript 文件保存在名为 public 的目录中,您可以这样做:
app.use(express.static('public'));
我们将一些图像保存在 **public/images** 子目录中,如下所示:
node_modules server.js public/ public/images public/images/logo.png
让我们修改“Hello World”应用程序,添加处理静态文件的功能。
var express = require('express'); var app = express(); app.use(express.static('public')); app.get('/', function (req, res) { res.send('Hello World'); }) var server = app.listen(8081, function () { var host = server.address().address var port = server.address().port console.log("Example app listening at http://%s:%s", host, port) })
将以上代码保存到名为 server.js 的文件中,并使用以下命令运行它。
$ node server.js
现在在任何浏览器中打开 http://127.0.0.1:8081/images/logo.png 并查看以下结果。
GET 方法
这是一个简单的示例,它使用 HTML FORM GET 方法传递两个值。我们将使用 server.js 内部的 **process_get** 路由器来处理此输入。
<html> <body> <form action = "http://127.0.0.1:8081/process_get" method = "GET"> First Name: <input type = "text" name = "first_name"> <br> Last Name: <input type = "text" name = "last_name"> <input type = "submit" value = "Submit"> </form> </body> </html>
让我们将上述代码保存在 index.htm 中,并修改 server.js 以处理主页请求以及 HTML 表单发送的输入。
var express = require('express'); var app = express(); app.use(express.static('public')); app.get('/index.htm', function (req, res) { res.sendFile( __dirname + "/" + "index.htm" ); }) app.get('/process_get', function (req, res) { // Prepare output in JSON format response = { first_name:req.query.first_name, last_name:req.query.last_name }; console.log(response); res.end(JSON.stringify(response)); }) var server = app.listen(8081, function () { var host = server.address().address var port = server.address().port console.log("Example app listening at http://%s:%s", host, port) })
使用 *http://127.0.0.1:8081/index.htm* 访问 HTML 文档将生成以下表单:
现在您可以输入名字和姓氏,然后单击提交按钮查看结果,它应该返回以下结果:
{"first_name":"John","last_name":"Paul"}
POST 方法
这是一个简单的示例,它使用 HTML FORM POST 方法传递两个值。我们将使用 server.js 内部的 **process_get** 路由器来处理此输入。
<html> <body> <form action = "http://127.0.0.1:8081/process_post" method = "POST"> First Name: <input type = "text" name = "first_name"> <br> Last Name: <input type = "text" name = "last_name"> <input type = "submit" value = "Submit"> </form> </body> </html>
让我们将上述代码保存在 index.htm 中,并修改 server.js 以处理主页请求以及 HTML 表单发送的输入。
var express = require('express'); var app = express(); var bodyParser = require('body-parser'); // Create application/x-www-form-urlencoded parser var urlencodedParser = bodyParser.urlencoded({ extended: false }) app.use(express.static('public')); app.get('/index.htm', function (req, res) { res.sendFile( __dirname + "/" + "index.htm" ); }) app.post('/process_post', urlencodedParser, function (req, res) { // Prepare output in JSON format response = { first_name:req.body.first_name, last_name:req.body.last_name }; console.log(response); res.end(JSON.stringify(response)); }) var server = app.listen(8081, function () { var host = server.address().address var port = server.address().port console.log("Example app listening at http://%s:%s", host, port) })
使用 *http://127.0.0.1:8081/index.htm* 访问 HTML 文档将生成以下表单:
现在您可以输入名字和姓氏,然后单击提交按钮查看以下结果:
{"first_name":"John","last_name":"Paul"}
文件上传
以下 HTML 代码创建了一个文件上传表单。此表单的 method 属性设置为 **POST**,enctype 属性设置为 **multipart/form-data**
<html> <head> <title>File Uploading Form</title> </head> <body> <h3>File Upload:</h3> Select a file to upload: <br /> <form action = "http://127.0.0.1:8081/file_upload" method = "POST" enctype = "multipart/form-data"> <input type="file" name="file" size="50" /> <br /> <input type = "submit" value = "Upload File" /> </form> </body> </html>
让我们将上述代码保存在 index.htm 中,并修改 server.js 以处理主页请求以及文件上传。
var express = require('express'); var app = express(); var fs = require("fs"); var bodyParser = require('body-parser'); var multer = require('multer'); app.use(express.static('public')); app.use(bodyParser.urlencoded({ extended: false })); app.use(multer({ dest: '/tmp/'})); app.get('/index.htm', function (req, res) { res.sendFile( __dirname + "/" + "index.htm" ); }) app.post('/file_upload', function (req, res) { console.log(req.files.file.name); console.log(req.files.file.path); console.log(req.files.file.type); var file = __dirname + "/" + req.files.file.name; fs.readFile( req.files.file.path, function (err, data) { fs.writeFile(file, data, function (err) { if( err ) { console.log( err ); } else { response = { message:'File uploaded successfully', filename:req.files.file.name }; } console.log( response ); res.end( JSON.stringify( response ) ); }); }); }) var server = app.listen(8081, function () { var host = server.address().address var port = server.address().port console.log("Example app listening at http://%s:%s", host, port) })
使用 *http://127.0.0.1:8081/index.htm* 访问 HTML 文档将生成以下表单:
File Upload: Select a file to upload:
NOTE: This is just dummy form and would not work, but it must work at your server.
Cookie 管理
您可以向 Node.js 服务器发送 Cookie,该服务器可以使用以下中间件选项进行处理。以下是一个简单的示例,用于打印客户端发送的所有 Cookie。
var express = require('express') var cookieParser = require('cookie-parser') var app = express() app.use(cookieParser()) app.get('/', function(req, res) { console.log("Cookies: ", req.cookies) }) app.listen(8081)
Node.js - RESTful API
什么是 REST 架构?
REST 代表 REpresentational State Transfer(表述性状态转移)。REST 是基于 Web 标准的架构,并使用 HTTP 协议。它围绕资源展开,其中每个组件都是一个资源,并且可以使用 HTTP 标准方法通过通用接口访问资源。REST 最初由 Roy Fielding 于 2000 年提出。
REST 服务器只需提供对资源的访问,而 REST 客户端则使用 HTTP 协议访问和修改资源。这里每个资源都由 URI/全局 ID 标识。REST 使用各种表示来表示资源,例如文本、JSON、XML,但 JSON 是最流行的一种。
HTTP 方法
以下四种 HTTP 方法通常用于基于 REST 的架构。
**GET** - 用于提供对资源的只读访问。
**PUT** - 用于创建新资源。
**DELETE** - 用于删除资源。
**POST** - 用于更新现有资源或创建新资源。
RESTful Web 服务
Web 服务是一组开放协议和标准,用于在应用程序或系统之间交换数据。用各种编程语言编写的并在各种平台上运行的软件应用程序可以使用 Web 服务通过计算机网络(如互联网)交换数据,其方式类似于在单个计算机上进行进程间通信。这种互操作性(例如,Java 和 Python 之间的通信,或 Windows 和 Linux 应用程序之间的通信)是由于使用了开放标准。
基于 REST 架构的 Web 服务称为 RESTful Web 服务。这些 Web 服务使用 HTTP 方法来实现 REST 架构的概念。RESTful Web 服务通常定义一个 URI(统一资源标识符)服务,该服务提供资源表示(例如 JSON)和一组 HTTP 方法。
为图书馆创建 RESTful API
假设我们有一个基于 JSON 的用户数据库,在文件 **users.json** 中有以下用户:
{ "user1" : { "name" : "mahesh", "password" : "password1", "profession" : "teacher", "id": 1 }, "user2" : { "name" : "suresh", "password" : "password2", "profession" : "librarian", "id": 2 }, "user3" : { "name" : "ramesh", "password" : "password3", "profession" : "clerk", "id": 3 } }
基于此信息,我们将提供以下 RESTful API。
序号 | URI | HTTP 方法 | POST 请求体 | 结果 |
---|---|---|---|---|
1 | listUsers | GET | 空 | 显示所有用户的列表。 |
2 | addUser | POST | JSON 字符串 | 添加新用户的详细信息。 |
3 | deleteUser | DELETE | JSON 字符串 | 删除现有用户。 |
4 | :id | GET | 空 | 显示用户的详细信息。 |
我将所有示例的大部分内容都以硬编码的形式保留,假设您已经知道如何使用 Ajax 或简单的表单数据从前端传递值,以及如何使用 express **Request** 对象处理它们。
列出用户
让我们在 server.js 文件中使用以下代码实现我们的第一个 RESTful API **listUsers**:
server.js
var express = require('express'); var app = express(); var fs = require("fs"); app.get('/listUsers', function (req, res) { fs.readFile( __dirname + "/" + "users.json", 'utf8', function (err, data) { console.log( data ); res.end( data ); }); }) var server = app.listen(8081, function () { var host = server.address().address var port = server.address().port console.log("Example app listening at http://%s:%s", host, port) })
现在尝试使用任何 REST 客户端在本地机器上使用 *URL: http://127.0.0.1:8081/listUsers* 和 *HTTP 方法: GET* 来访问已定义的 API。这应该会产生以下结果:
当您将解决方案部署到生产环境时,您可以更改给定的 IP 地址。
{ "user1" : { "name" : "mahesh", "password" : "password1", "profession" : "teacher", "id": 1 }, "user2" : { "name" : "suresh", "password" : "password2", "profession" : "librarian", "id": 2 }, "user3" : { "name" : "ramesh", "password" : "password3", "profession" : "clerk", "id": 3 } }
添加用户
以下 API 将向您展示如何在列表中添加新用户。以下是新用户的详细信息:
user = { "user4" : { "name" : "mohit", "password" : "password4", "profession" : "teacher", "id": 4 } }
您可以使用 Ajax 调用以 JSON 格式接受相同的输入,但出于教学目的,我们在这里对其进行了硬编码。以下是将新用户添加到数据库中的 **addUser** API:
server.js
var express = require('express'); var app = express(); var fs = require("fs"); var user = { "user4" : { "name" : "mohit", "password" : "password4", "profession" : "teacher", "id": 4 } } app.post('/addUser', function (req, res) { // First read existing users. fs.readFile( __dirname + "/" + "users.json", 'utf8', function (err, data) { data = JSON.parse( data ); data["user4"] = user["user4"]; console.log( data ); res.end( JSON.stringify(data)); }); }) var server = app.listen(8081, function () { var host = server.address().address var port = server.address().port console.log("Example app listening at http://%s:%s", host, port) })
现在尝试使用任何 REST 客户端在本地机器上使用 *URL: http://127.0.0.1:8081/addUser* 和 *HTTP 方法: POST* 来访问已定义的 API。这应该会产生以下结果:
{ "user1":{"name":"mahesh","password":"password1","profession":"teacher","id":1}, "user2":{"name":"suresh","password":"password2","profession":"librarian","id":2}, "user3":{"name":"ramesh","password":"password3","profession":"clerk","id":3}, "user4":{"name":"mohit","password":"password4","profession":"teacher","id":4} }
显示详细信息
现在我们将实现一个 API,该 API 将使用用户 ID 调用,并将显示相应用户的详细信息。
server.js
var express = require('express'); var app = express(); var fs = require("fs"); app.get('/:id', function (req, res) { // First read existing users. fs.readFile( __dirname + "/" + "users.json", 'utf8', function (err, data) { var users = JSON.parse( data ); var user = users["user" + req.params.id] console.log( user ); res.end( JSON.stringify(user)); }); }) var server = app.listen(8081, function () { var host = server.address().address var port = server.address().port console.log("Example app listening at http://%s:%s", host, port) })
现在尝试使用任何 REST 客户端在本地机器上使用 *URL: http://127.0.0.1:8081/2* 和 *HTTP 方法: GET* 来访问已定义的 API。这应该会产生以下结果:
{"name":"suresh","password":"password2","profession":"librarian","id":2}
删除用户
此 API 与 addUser API 非常相似,我们通过 req.body 接收输入数据,然后根据用户 ID 从数据库中删除该用户。为了使我们的程序更简单,我们假设我们将删除 ID 为 2 的用户。
server.js
var express = require('express'); var app = express(); var fs = require("fs"); var id = 2; app.delete('/deleteUser', function (req, res) { // First read existing users. fs.readFile( __dirname + "/" + "users.json", 'utf8', function (err, data) { data = JSON.parse( data ); delete data["user" + 2]; console.log( data ); res.end( JSON.stringify(data)); }); }) var server = app.listen(8081, function () { var host = server.address().address var port = server.address().port console.log("Example app listening at http://%s:%s", host, port) })
现在尝试使用任何 REST 客户端在本地机器上使用 *URL: http://127.0.0.1:8081/deleteUser* 和 *HTTP 方法: DELETE* 来访问已定义的 API。这应该会产生以下结果:
{"user1":{"name":"mahesh","password":"password1","profession":"teacher","id":1}, "user3":{"name":"ramesh","password":"password3","profession":"clerk","id":3}}
Node.js - 应用程序扩展
Node.js 以单线程模式运行,但它使用事件驱动范例来处理并发。它还促进创建子进程,以利用基于多核 CPU 的系统上的并行处理。
子进程始终具有三个流 **child.stdin**、**child.stdout** 和 **child.stderr**,这些流可能与父进程的 stdio 流共享。
Node 提供了 **child_process** 模块,该模块有以下三种主要方法来创建子进程。
**exec** - child_process.exec 方法在 shell/控制台中运行命令并缓冲输出。
**spawn** - child_process.spawn 使用给定的命令启动一个新进程。
**fork** - child_process.fork 方法是 spawn() 的一个特例,用于创建子进程。
exec() 方法
child_process.exec 方法在 shell 中运行命令并缓冲输出。它具有以下签名:
child_process.exec(command[, options], callback)
参数
以下是所用参数的描述 −
**command** (字符串) 要运行的命令,带空格分隔的参数
**options** (对象) 可能包含以下一个或多个选项:
**cwd** (字符串) 子进程的当前工作目录
**env** (对象) 环境键值对
**encoding** (字符串) (默认值:'utf8')
**shell** (字符串) 用于执行命令的 shell (UNIX 上默认为 '/bin/sh',Windows 上默认为 'cmd.exe',shell 应该理解 UNIX 上的 -c 开关或 Windows 上的 /s /c。在 Windows 上,命令行解析应该与 cmd.exe 兼容。)
**timeout** (数字) (默认值:0)
**maxBuffer** (数字) (默认值:200*1024)
**killSignal** (字符串) (默认值:'SIGTERM')
**uid** (数字) 设置进程的用户身份。
**gid** (数字) 设置进程的组身份。
**callback** 当进程终止时,该函数将获得三个参数 **error**、**stdout** 和 **stderr**,并使用输出调用它们。
exec() 方法返回一个具有最大大小的缓冲区,并等待进程结束,并尝试一次返回所有缓冲数据。
示例
让我们创建两个名为 support.js 和 master.js 的 js 文件:
文件:support.js
console.log("Child Process " + process.argv[2] + " executed." );
文件:master.js
const fs = require('fs'); const child_process = require('child_process'); for(var i=0; i<3; i++) { var workerProcess = child_process.exec('node support.js '+i,function (error, stdout, stderr) { if (error) { console.log(error.stack); console.log('Error code: '+error.code); console.log('Signal received: '+error.signal); } console.log('stdout: ' + stdout); console.log('stderr: ' + stderr); }); workerProcess.on('exit', function (code) { console.log('Child process exited with exit code '+code); }); }
现在运行 master.js 查看结果:
$ node master.js
验证输出。服务器已启动。
Child process exited with exit code 0 stdout: Child Process 1 executed. stderr: Child process exited with exit code 0 stdout: Child Process 0 executed. stderr: Child process exited with exit code 0 stdout: Child Process 2 executed.
spawn() 方法
child_process.spawn 方法使用给定的命令启动一个新进程。它具有以下签名:
child_process.spawn(command[, args][, options])
参数
以下是所用参数的描述 −
**command** (字符串) 要运行的命令
**args** (数组) 字符串参数列表
**options** (对象) 可能包含以下一个或多个选项:
**cwd** (字符串) 子进程的当前工作目录。
**env** (对象) 环境键值对。
**stdio** (数组) 字符串 子进程的 stdio 配置。
**customFds** (数组) 已弃用 子进程要使用的 stdio 文件描述符。
**detached** (布尔值) 子进程将成为进程组领导者。
**uid** (数字) 设置进程的用户身份。
**gid** (数字) 设置进程的组身份。
spawn() 方法返回流 (stdout & stderr),当进程返回大量数据时应使用它。spawn() 在进程开始执行时就开始接收响应。
示例
创建两个名为 support.js 和 master.js 的 js 文件:
文件:support.js
console.log("Child Process " + process.argv[2] + " executed." );
文件:master.js
const fs = require('fs'); const child_process = require('child_process'); for(var i = 0; i<3; i++) { var workerProcess = child_process.spawn('node', ['support.js', i]); workerProcess.stdout.on('data', function (data) { console.log('stdout: ' + data); }); workerProcess.stderr.on('data', function (data) { console.log('stderr: ' + data); }); workerProcess.on('close', function (code) { console.log('child process exited with code ' + code); }); }
现在运行 master.js 查看结果:
$ node master.js
验证输出。服务器已启动
stdout: Child Process 0 executed. child process exited with code 0 stdout: Child Process 1 executed. stdout: Child Process 2 executed. child process exited with code 0 child process exited with code 0
fork() 方法
child_process.fork 方法是 spawn() 的一个特例,用于创建 Node 进程。它的签名如下:
child_process.fork(modulePath[, args][, options])
参数
以下是所用参数的描述 −
modulePath (字符串) 子进程中要运行的模块。
**args** (数组) 字符串参数列表
**options** (对象) 可能包含以下一个或多个选项:
**cwd** (字符串) 子进程的当前工作目录。
**env** (对象) 环境键值对。
execPath (字符串) 用于创建子进程的可执行文件。
execArgv (数组) 传递给可执行文件的字符串参数列表 (默认值:process.execArgv)。
silent (布尔值) 如果为 true,则子进程的 stdin、stdout 和 stderr 将被管道传输到父进程,否则它们将继承自父进程,详情请参阅 spawn() 的 stdio 的“pipe”和“inherit”选项 (默认值为 false)。
**uid** (数字) 设置进程的用户身份。
**gid** (数字) 设置进程的组身份。
fork 方法除了拥有普通 ChildProcess 实例中的所有方法外,还返回一个带有内置通信通道的对象。
示例
创建两个名为 support.js 和 master.js 的 js 文件:
文件:support.js
console.log("Child Process " + process.argv[2] + " executed." );
文件:master.js
const fs = require('fs'); const child_process = require('child_process'); for(var i=0; i<3; i++) { var worker_process = child_process.fork("support.js", [i]); worker_process.on('close', function (code) { console.log('child process exited with code ' + code); }); }
现在运行 master.js 查看结果:
$ node master.js
验证输出。服务器已启动。
Child Process 0 executed. Child Process 1 executed. Child Process 2 executed. child process exited with code 0 child process exited with code 0 child process exited with code 0
Node.js - 打包
JXcore 是一个开源项目,它引入了一个独特的特性,用于将源文件和其他资源打包并加密到 JX 包中。
假设您有一个包含许多文件的大型项目。JXcore 可以将它们全部打包到单个文件中,以简化分发。本章将快速概述整个过程,从安装 JXcore 开始。
JXcore 安装
安装 JXcore 非常简单。这里我们提供了在您的系统上安装 JXcore 的分步说明。请按照以下步骤操作:
步骤 1
根据您的操作系统和机器架构,从 https://github.com/jxcore/jxcore 下载 JXcore 包。我们下载了一个在 64 位机器上运行的 Cenots 包。
$ wget https://s3.amazonaws.com/nodejx/jx_rh64.zip
步骤 2
解压下载的文件 jx_rh64.zip,并将 jx 二进制文件复制到 /usr/bin 或根据您的系统设置复制到任何其他目录。
$ unzip jx_rh64.zip $ cp jx_rh64/jx /usr/bin
步骤 3
适当设置 PATH 变量,以便您可以从任何位置运行 jx。
$ export PATH=$PATH:/usr/bin
步骤 4
您可以通过发出如下所示的简单命令来验证您的安装。您应该发现它正在工作并打印其版本号,如下所示:
$ jx --version v0.10.32
代码打包
假设您的项目具有以下目录,您将所有文件(包括 Node.js、主文件 index.js 和所有本地安装的模块)都保存在这些目录中。
drwxr-xr-x 2 root root 4096 Nov 13 12:42 images -rwxr-xr-x 1 root root 30457 Mar 6 12:19 index.htm -rwxr-xr-x 1 root root 30452 Mar 1 12:54 index.js drwxr-xr-x 23 root root 4096 Jan 15 03:48 node_modules drwxr-xr-x 2 root root 4096 Mar 21 06:10 scripts drwxr-xr-x 2 root root 4096 Feb 15 11:56 style
要打包上述项目,您只需进入此目录并发出以下 jx 命令。假设 index.js 是 Node.js 项目的入口文件:
$ jx package index.js index
您可以使用任何其他包名称代替 index。我们使用 index 是因为我们希望将主文件名保留为 index.jx。但是,上述命令将打包所有内容,并创建以下两个文件:
index.jxp 这是一个中间文件,包含编译项目所需完整的项目详细信息。
index.jx 这是一个二进制文件,包含已准备好交付给您的客户端或生产环境的完整包。
启动 JX 文件
假设您的原始 Node.js 项目运行如下:
$ node index.js command_line_arguments
使用 JXcore 编译您的包后,可以按如下方式启动它:
$ jx index.jx command_line_arguments
要了解有关 JXcore 的更多信息,您可以查看其官方网站。