nodejs基础

 

 

nodejs(二) --- 重要知识点回顾

1. 运行一个nodejs文件

如一个js文件中只含有console.log("hello world");的文件,我们再git里运行node,即 node hello.js 即可发送输出hello world。如下:

nodejs基础

 

2. 交互模式

直接输入node,即进入node环境,即可输入任何语句

nodejs基础

 

 

3. 创建一个简单的服务器

创建server.js -> require http模块 -> 调用 createServer 方法进行创建 -> 监听某个端口 -> 运行js文件(实际上是在运行这个node服务器) -> 在浏览器中发送请求。

js文件如下:

nodejs基础

其中比较重要的是要知道createServer这个内置函数就是用于创建服务器的,然后接受一个函数作为参数,我们可以用writeHead方法来写头部,使用end方法来输出内容。用listen来监听端口号。当然,也可以是监听8889等端口号,只要是合法的就行。在浏览器端发出请求,结果如下:

nodejs基础

 

 

4. node中的npm

 

安装node时就已经安装了npm,即一个包管理工具,我们一般利用它来安装一些包,即npm install <package> 如果后面添加 -g ,那么就会将包安全到全局环境,即user路径下。如果不添加,就会安装在当前文件夹下,当然首先会创建node_modules文件,在此之下。

 

一般,我们要创建一个项目时,我们可以先npm init,通过它,我们就可以创建一个package.json文件,然后通过配置该文件说明我们的项目信息。其中主要的参数有:

(1)name --- 包名   (2)version --- 版本号  (3) description --- 包的描述   (4) homepage --- 包的官网url  (5) author --- 包的作者名字  (6) contributors --- 包的其他贡献者名字

(7)dependencies  --- 依赖包列表(如果依赖包没有安装,npm 会自动将依赖包安装在 node_module 目录下)    (8)repository --- 包代码存放的地方   (9)main --- main 字段是一个模块ID,它是一个指向你程序的主要项目。就是说,如果你包的名字叫 express,然后用户安装它,然后require("express")    (9) keywords --- 关键字

 

卸载模块: npm uninstall <package>

 

更新模块: npm update <package>

 

版本号相关: 当使用npm下载和发布代码时,都会涉及到版本号相关,即X-Y-Z,一般而言,只有当版本发生了重大的变化,不向下兼容时,X才会变化; 如果增加了新的功能,仍旧向下兼容 Y 发生变化; 如果仅仅是做了很小的改变,如修复了bug,那么 Z 发生变化。

 

如果希望知道某条命令的详细信息,如install的,可以输入 npm help install

 

npm连接的是国外的网站,也可以使用淘宝镜像:

1

npm install -g cnpm --registry=https://registry.npm.taobao.org

 然后就可以使用cnpm来安装了。

 

 

5. nodejs REPL

即node的交互式解释器,即我们输入node之后即进入node环境,这个node环境就是node的交互式解释器。

值得注意的是,和console控制台不同,REPL支持多行输入,如下所示:

 

$ 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
>

 

一般我们使用ctrl + c来退出该环境,大多数情况下,我们都是用ctrl+c来退出环境的。不论是否是node。

 

 

6. nodejs回调函数

在node中,我们知道其最大的特点就是异步I/O,而实现异步I/O的关键就在于回调函数。

首先,我们看看同步的是什么样:

var fs = require("fs");
var data = fs.readFileSync('test.txt');
console.log(data.toString());
console.log("over");

这是main.js中的代码,即先引入fs模块,然后才能实用相应的API,readFileAsync()函数用于同步读取文件,它是阻塞的,返回这个读取的文件,输出如下:

nodejs基础

 

下面这个一个异步的,

nodejs基础

    var fs = require("fs");
    fs.readFile("test.txt",function (err, data) {
        if (err) {
            console.log(err);
        } else {
            console.log(data.toString());
        }
    });
    console.log("over");

nodejs基础

结果如下:

nodejs基础

即readFile是一个异步的,在执行这条语句的时候不会阻塞后面的语句,而是在读完文件之后再console。

即我们在读取代码的时候可以做下面的很多事情,这样就可以节省很多时间。

readFile和readFileSync都接受第二个参数,即编码类型,如"utf-8"

 

 

 

7. nodejs事件循环

nodejs事件循环利用的是观察者模式,也就是发布订阅模式。简单的理解,DOM元素绑定事件就是这样的模式。其中绑定的元素是发布者,函数是订阅者,当元素发生了变化时(被点击等),就会通知所有的订阅者。 

nodejs使用事件驱动模型,当服务器接受到了请求之后,就会关闭这个请求,然后再处理,为的是等待下一个请求。这样,请求就不会被耽搁。这个模型的效率非常高,因为他一直在接受请求,而没有等待任何读写操作。在事件驱动模型中,会生成一个主循环来监听事件,当检测到事件时触发回调函数:

nodejs基础

虽然这里没有所谓的DOM元素,但是实现应当是一样的,即观察者模式的最好理解是好莱坞电影中的一句话: 你不要打电话给我,我会打电话给你

在node中我们常常使用events模块来实现,即首先引入events,然后创建一个对象,利用这个对象的on方法绑定时间,利用对象的emit方法来触发事件,如下所示:

nodejs基础

var events = require("events");
var eventEmitter = new events.EventEmitter();
eventEmitter.on("selfDefine", function () {
    console.log("This is selfDefine1");
});
eventEmitter.on("selfDefine", function () {
    console.log("This is selfDefine2");
});
eventEmitter.on("selfDefine", function () {
    console.log("This is selfDefine3");
});
eventEmitter.on("selfDefine", function () {
    console.log("This is selfDefine4");
});
eventEmitter.emit("selfDefine");
console.log("over");

nodejs基础

最终效果如下:

nodejs基础

在这里,我们就可以认为这是发布订阅者模式,首先可以知道发布者是selfDefine事件,订阅者是4个,一旦selfDefine被触发,那么就会通知订阅者,方法是将订阅者添加到了Event Loop中去,然后通过事件循环来监听,一旦被触发,就会通知,即我给你打电话,你没有给我打电话。

当事件触发时,注册到这个事件的事件监听器被依次调用。

 

另外,在on和emit中是可以传递参数的,如下所示:

nodejs基础

var events = require("events");
var eventEmitter = new events.EventEmitter();
eventEmitter.on("selfDefine", function (x) {
    console.log("This is selfDefine1 " + x);
});
eventEmitter.on("selfDefine", function (x) {
    console.log("This is selfDefine2 " + x);
});
eventEmitter.on("selfDefine", function (x) {
    console.log("This is selfDefine3 " + x);
});
eventEmitter.on("selfDefine", function (x) {
    console.log("This is selfDefine4 " + x);
});
eventEmitter.emit("selfDefine", "argument");
console.log("over");

nodejs基础

最终的效果如下:

nodejs基础

 

 

关于 EventEmitter 还有其他的属性,如下所示:

addListener(event, listener) --- 它和on是类似的,都是添加某一个事件的监听器

removeListener(event, listener) --- 即通过此API可以将监听器取消(特定的listener)。

removeAllListeners(event) --- 可以取消event下的所有监听器。

newListener(event, listener); --- 该事件在添加新的监听器时被触发。

listenerCount(emitter, event); --- 返回指定监听器的数量。

listeners(event) --- 返回指定事件的监听器数组。

 

once(event, listener) --- 通过once就可以知道,这个监听器只会监听一次,后面再调用,就不会监听了。

举例如下:

nodejs基础

var events = require("events");
var eventEmitter = new events.EventEmitter();
eventEmitter.on("foo", function () {
    console.log("via on");
});
eventEmitter.once("foo", function () {
    console.log("via once");
});
eventEmitter.emit("foo");
setTimeout(function () {
    eventEmitter.emit("foo");
}, 1000);

nodejs基础

 

最终的执行效果如下:

nodejs基础

在执行过程中vai on和via once是同时出现的,过了1s之后,via on 出现, via once不再出现,因为通过once添加的监听器只会监听一次,然后就被销毁了(即后面不再监听)。

 

 

 

 

8. Nodejs Buffer (缓冲区)

作为服务器端语言的nodejs,自然会接受请求,如TCP请求,都是通过二进制来传递的,但是js语言本身并没有接受二进制的api,所以nodejs中添加了Buffer类来作为存储二进制数据的缓冲区。

通过Buffer类创建buffer实例的几种方法:
1. 创建长度为10字节(1 B = 8 bit)的Buffer类, var buf = new Buffer(10);

2. 通过数组创建Buffer实例, var buf = new Buffer([10, 20, 30, 15]);

3. 通过一个字符串来创建buffer实例, var buf = new Buffer("i love coding", "utf-8"); 注意: 我们这里使用utf-8格式编码,还可以是"ascii",  "utf16le", "ucs2", "base64" 和 "hex",当然,默认就是utf-8。

已经有了buffer实例,我们就可以使用buffer实例的一些方法了,如下所示:

  • write()(写入数据) --- buf.write(string[, offset[, length]][, encoding])。它的返回值是写入的长度。  我们知道[]表示式可选的, 其中string是将要写入的字符串; offset是缓冲区开始写入的索引值,默认为0;length是长度,默认是buf.length; encoding是编码方式,默认是utf-8。
  • toString() (读取数据)--- buf.toString([encoding[, start[, end]]])。它的返回值是读取的值。其中的encoding表示读取数据的编码方式, start和end表示读取数据的位置。
  • toJSON() (转换为JSON对象)--- buf.toJSON(buf)。 返回值是一个JSON对象。
  • Buffer.concat(list[,totalLength]) (合并Buffer对象) --- 返回值是合并后的buffer对象。其中list是一个数组,其中的每个元素是一个buffer实例,totalLength是在制定合并之后的总长度。
  • buf.compare(otherBuffer) (缓冲区大小比较) ---  比较两个缓冲区的大小,返回 0 -1 1 。
  • buf.copy(targetBuffer[, targetStart[, sourceStart[, sourceEnd]]]) (缓冲区的拷贝
  • buf.slice([start[, end]])(缓冲区的裁剪)
  • buf.length() --- 返回缓冲区的长度。
  • ......

 

9. Nodejs Stream(流)

  Stream是一个抽象的接口,并且它是eventEmitter的实例,通常Stream有四种流类型, 包括可读、可写、可读可写、操作被写入然后读出。

  既然它是eventEmitter的实例,那么就会有事件,这个事件当然不再是自定义然后使用emit的方式,而是已经定义的,不再需要emit了。 有 data 、end、error、finish。其中data表示只要发现有数据就会立即触发, end表示没有更多的数据可读时就会触发, error是在读或写发生错误的时候触发,finish在所有的数据被写入底层系统时触发。

  因为流中的读写都是与文件相关,所以需要引入fs模块。读:

nodejs基础

var fs = require("fs");
var readStream = fs.createReadStream("./test.txt");
readStream.setEncoding("utf-8");
var data = "";
readStream.on("data", function (chunk) {
    data += chunk;
});
readStream.on("end", function () {
    console.log(data +" FINISHED");
});
readStream.on("error", function (err) {
    console.log(err);
});

nodejs基础

  可以看到首先引入文件系统fs,然后使用 createReadStream()方法来读取文件, 绑定了data之后,只要文件中有内容就会被触发,当读取文件内容结束之后,就会执行end下的监听器。 在读的过程中有错,就会执行error下的监听器。

  

  下面是写操作:(其中的test.js现在内容为空)

nodejs基础

var fs = require("fs");
var writeStream = fs.createWriteStream("./test.txt");
var data =  "I want to write something";
writeStream.write(data);
writeStream.end();
writeStream.on("finish", function () {
    console.log("finished");
});
writeStream.on("error", function (err) {
    console.log(err);
});
console.log("ok!");

nodejs基础

 

   最终输出为: ok!   finished

  注意: 我们需要使用end()方法表示结束,然后当写入完成之后就会触发finish, 最后,我们打开test.txt就会发现确实已经写入了data数据 。

 

管道流:它提供了这样的一个机制 --- 从一个流中读取数据,然后输入到另一个流中。

nodejs基础

var fs = require("fs");
var readStream = fs.createReadStream("./input.txt");
var writeStream = fs.createWriteStream("./output.txt");
readStream.pipe(writeStream);
console.log("finished");

 

 

通过这种方式,我们就可以将input.txt中的内容流到output.txt之中了。

 

链式流:链式是通过连接输出流到另外一个流并创建多个对个流操作链的机制。链式流一般用于管道操作。

引入zlib模块进行压缩文件, 之所以说是链式流,是因为我们可以连续使用pipe(),如下所示:

var fs = require("fs");
var zlib = require("zlib");
var readStream = fs.createReadStream("./foo.txt");
readStream.pipe(zlib.createGzip()).pipe(fs.createWriteStream("./foo.min.txt"));

 

即我们首先引入fs模块和压缩库zlib,然后再创建一个读流,通过管道流pipe到压缩文件,然后再pipe到一个将要保存压缩文件的写文件。最后可以看到foo.min.txt是被压缩过的。(注意:其中foo.min.txt不需要自己来写,他会自动生成)

当然我们还可以用createGunzip()方法来解压缩。

 

 

 

10. nodejs模块系统

一个nodejs文件就是一个模块。我们之前使用var fs = require("fs"); 这里就是引入了一个fs模块。因为fs是内置的,所以直接引入就好。但是如果是我们自己创建的一个foo.js文件,我们希望引用这个模块,就可以使用var foo = require("./foo"); 即引入当然文件目录下的foo.js中的模块。 其中js是默认的,省略不写。

我们接触到的require是nodejs提供的一个接受对象,与之相对的是exports倒出对象。

比如我们创建一个foo.js,这就是一个模块,内容如下:

function Foo() {
    this.sayHello = function () {
        console.log("hello world!");
    }
}
module.exports = Foo;

 

即这导出了一个构造函数。 然后我们就可以引入这个module了,如下:

var Foo = require("./foo");
var myFoo = new Foo();
myFoo.sayHello()

 

这里我们require到了这个模块,然后创建了实例,调用了模块的方法。

 

服务器端的模块:之前我们使用的require("http")就是在引入服务器端的模块。 然后再直接调用即可,如createServer()方法。

nojs加载模块方式如下:

nodejs基础

第一步: 判断文件模块缓存区中是否存在模块。(对于我们之前加载过的模块,会缓存到缓存区中,下次最先查找并加载)。

第二步: 判断是否是原生模块,如http、fs等这就是原生模块。 如果判断一个模块是原生模块就会优先加载原生模块。即即使我们有了一个http.json文件,但是由于原生模块的优先级更高,所以优先加载。

第三步: 判断是否是自定义的模块。 即我们自己设定的模块。

 

 

 

 

11. nodejs函数

  nodejs中的函数也可以作为另一个函数的参数,同样也有匿名函数的概念,如下所示:

var http = require("http");
http.createServer(function (request, response) {
    response.writeHead("200", {"Content-Type": "text/plain"});
    response.write("zhuzhenwei,you are handsome!");
    response.end();
}).listen(8888);

  这样就创建了一个服务器。

  注意:其中在writeHead中最好写上charset=utf-8; 后面的字符也可以是utf8、UTF8、UTF-8。 他们都是有效的的。

  例如下面的这个函数,和js中的作用域是一样的,注意其中我们需要将Content-Type的内容写成text/plain; charset=utf8; 否则在浏览器中输出汉字时会有问题。

nodejs基础

var http = require("http");
http.createServer(function (req, res) {
            res.writeHead(200, {"Content-Type": "text/html; charset=utf8"});
            if (req.url !== "/favicon.ico") { // 清除第二次访问
                console.log("访问");
                a(res);
                res.write("Hello world!");
                res.end();  
            }
}).listen(8081);
function a(res) {
    res.write("hello, 我是一个被调用的函数。");
}

console.log("Server running at http:127.0.0.1:8081");

nodejs基础

  其中的req.url !== "/favicon.ico"是为了解决自身的bug的。 

 

 

上面的这种方式是对于一个内部的函数而言的,但是如果我们希望是一个外部文件的函数应该怎么办呢? 

如下,建立一个fun2.js,和server.js在同一个文件下,内容如下:

function a2(res) {
    res.write("我是fun2调用的函数");
}
module.exports = a2;

注意:最后一句的意思是我们希望将这个函数导出,如果不导出去,就没有办法使用。然后server.js内容如下:

nodejs基础

var http = require("http");
var otherfun = require("./fun2");
http.createServer(function (req, res) {
            res.writeHead(200, {"Content-Type": "text/html; charset=utf8"});
            if (req.url !== "/favicon.ico") { // 清除第二次访问
                console.log("访问");
                otherfun(res);
                res.write("Hello world!");
                res.end();  
            }
}).listen(8081);


console.log("Server running at http:127.0.0.1:8081");

nodejs基础

注意: 其中如果我们要使用这个函数,就必须要用otherfun来调用,虽然在fun2中的函数名是fun2,但是在server.js中只认otherfun。 

  另外,因为这是一个本地的文件,我们最好在前面加上./ 表示相对位置。

缺点:可以看到这样,我们每次只能在一个文件中导出一个函数,但是对于一个文件中有多个函数的情况应该怎么导出呢? 如下所示:

nodejs基础

module.exports = {
    func2 : function (res) {
        res.write("我是func2函数");
    },
    func3 : function (res) {
        res.write("我是func3函数");
    }
};

nodejs基础

即将函数使用对象的形式定义,然后我们导出这么个对象,就向http一样,我们引入这个http对象之后,然后使用http.createServer等方法。 

调用的时候显然就是下面这样的,调用对象的方法:

nodejs基础

var http = require("http");
var fun = require("./fun.js");
http.createServer(function (req, res) {
            res.writeHead(200, {"Content-Type": "text/html; charset=utf8"});
            if (req.url !== "/favicon.ico") { // 清除第二次访问
                console.log("访问");
                fun.func2(res);
                fun.func3(res);
                res.end();  
            }
}).listen(8081);


console.log("Server running at http:127.0.0.1:8081");

nodejs基础

效果如下:

nodejs基础

所以,可以看到,后者可以调用多个函数,一般就用后面这种形式。

另外,我们还经常用字符串的形式,如fun["func2"]或者fun["func3"],这样的好处是:我们可以把调用写活了,在后面讲到路由的时候更为重要,因为可以把字符串作为一个变量,输入不同的值,就可以调用不同的函数, 非常重要。。

 

  

12. nodejs路由(重点)

我们所需要的数据都在request对象中,另外,我们得先解析url,需要引入url模块和querystring模块。

nodejs基础

即url.parse()可以解析这个url。

对于客户端输入的url,我们通过request.url即可获取。

这里比较难理解。。。

 

13. nodejs全局对象

与浏览器中window作为全局不同,在node中global是全局对象,我们可以直接在全局对象上定义属性,那么就可以访问到了,指的注意的是,由于每一个nodejs模块都是一个作用域,所以直接var是局部变量,而不是全局变量。

__filename --- 这个全局变量表示运行的nodejs的文件名。

__dirname --- 表示目录名称(不包含文件名)

在文件中的代码如下:

console.log(__filename);
console.log(__dirname);

 

输出如下:

nodejs基础

可以看出,其中__filename是包含了路径的。 而__dirname仅仅是缺少了文件名,只有路径。

 

另外,console、setTimeout、clearInterval等等都是全局对象,举例如下:

nodejs基础

console.time("set");
var Timer = setInterval(function () {
    console.log(__dirname);
}, 1000);
setTimeout(function () {
    clearInterval(Timer);
    console.log("cleared");
},5000);
console.timeEnd("set");
console.info("info");
console.warn("warn");
console.log("my birthday is %d", 19950628);

nodejs基础

 

最终的输入如下:
nodejs基础

可以看到,由于setInterval和setTimeout是非阻塞的,所以后面的语句先执行,指的注意的是其中的console.log("%d",19950628)的应用,这与C语言中的printf是非常相似的。另外console.trace()可以追踪调用栈。

另外还有一个比较重要的api,即setImmediate(handler); 他是IE10中支持的。其他的浏览器一律不支持,但是node是支持的,这个解决单线程阻塞的问题,当然用setTimeout(handler, 0);也可以,但是后者的延迟时间较前者更长一些。

 

process也是一个全局变量,在node环境下输入global.process就可以看到其中具有的变量,因为process本身就是一个对象。不难理解process是描述进程(process即进程的意思)的一个全局对象。

他还有一些事件,如下:

nodejs基础

举例如下:

nodejs基础

setImmediate(function () {
    console.log("god");
});
process.on("exit", function (code) {
    console.log("exitCode is:", code);
});
console.log("over");

nodejs基础

 

 

即进程一旦结束,就会触发监听器。 注意:这里使用on的方式,所以我们可以认为process是eventEmitter的实例。

输入如下:

nodejs基础

退出码为0是什么意思呢?  因为每当exit事件触发,都会有一个code即退出码,表示这个退出的方式,0表示正常退出,一般还有如下几种退出码:

nodejs基础

 

process 不仅提供了上述事件,还提供了非常多的有关进程的属性,如pid(进程号)、platform(程序运行的平台)、archf(当前CPU的架构)、title(进程名,默认为node)、versions(包含了node的版本和依赖)、version(node的版本)、execPath(当前脚本的二进制文件路径)、stdin、stdout、stderr。举例如下:

console.log(process.platform);
console.log(process.version);
process.stdout.write("hello world \n");
console.log(process.execPath);

 

nodejs基础

注意:其中的stdout.write是在终端输出,那么什么时候才能在页面上输出呢?  显然,由于node是服务器端语言,所以说只能通过响应(response)的方式才能返回给客户端。

 

下面的process方法也是常用的:

console.log(process.memoryUsage());
console.log(process.cwd());

 

其中第一个是内存使用情况。后者是cwd(current working directory)即当前工作目录。

nodejs基础

可以看到rss、heapTotal、heapUsed表示了内存使用情况。

 

 

 

14. nodejs常用工具

这里需要介绍的工具,首先要引入util模块,主要有util.inherits、util.inspect、util.isArray(object)、util.isRegExp(object)、util.isDate(object)、util.isError(object)。

 

util.inherits(subconstructor, supconstructor),

即这个方法可以实现继承,但是这里的继承和我们使用js实现的继承也有不同之处,主要区别是这里的继承是原型对象之间的继承,而不会继承上一级的构造函数,举例如下:

nodejs基础

var util = require("util");
function Sup() {
    this.name = "sup";
    this.sayHello = function () {
        console.log(this.name);
    }
}
Sup.prototype.show = function () {
    console.log("just show yourself");
}
function Sub() {
    this.name = "sub";
}
util.inherits(Sub, Sup);
var objSup = new Sup();
console.log(objSup.name);
var objSub = new Sub();
objSub.show();
// objSub.sayHello(); // 报错, objSub.sayHello() is not a function.

nodejs基础

 

这里可以看到其中的objSub.show()成功继承了Sup的原型中的方法,但是objSub.sayHello()却会报错,因为通过util.inherits()的方式是不能继承构造函数中的方法的,与js中的不一样,需要注意。

 

util.inspect(obj)

这里并不是只有一个参数,它实际上还可以接受三个,第二个是true/false,表示是否显示更多的信息,第三个是depth,即递归的层数,默认是2层; 第四个是关于颜色的true/false。举例如下:

nodejs基础

var util = require("util");
function Foo() {
    this.age = 21;
    this.name = 'zzw';
    this.sayHello = function () {
        console.log(this.age);
    }
}
console.log(util.inspect(Foo));
console.log(util.inspect(Foo, true));

nodejs基础

 

最终的输出如下:
nodejs基础

可以看到,没有第二个参数,那么只会输出简单的一个函数,如果为true,就会输出更多深层次的内容。

 

 util.isArray()

实际上,这里和js中的Array.isArray()是一样的,举例如下:

var util = require("util");
console.log(util.isArray([])); //true
console.log(util.isArray(new Array()));//true
console.log(util.isArray({}));//true

 

 

util.isRegExp() 即判断是否是一个正则表达式

 

util.isDate() 即判断是否是一个日期

 

util.isError() 即判断是否是一个错误对象

 

 

15. nodejs 文件系统

nodejs作为后台语言,必然不可避免的需要和数据库文件等打交道,所以文件系统模块是非常必要的。即file system --- 文件系统。

在node中的fs中,所有的方法均有同步和异步之分。如同步读取文件,fs.readFile();异步读取文件,fs.readFileSync()。如下所示:

nodejs基础

var fs = require("fs");

// 异步读取
fs.readFile('input.txt', function (err, data) {
   if (err) {
       return console.error(err);
   }
   console.log("异步读取: " + data.toString());
});

// 同步读取
var data = fs.readFileSync('input.txt');
console.log("同步读取: " + data.toString());

console.log("程序执行完毕。");

nodejs基础

 

ok!  下面介绍一些node中常用的文件操作api。

打开文件

nodejs基础

var fs = require("fs");
console.log("准备打开文件");
fs.open("foo.txt","r+",function (err, fd) {
    if (err) {
        return  console.log(err);
    } 
    console.log("成功打开文件");  

});

nodejs基础

 

即第一个参数是要打开的文件的路径,第二个参数是打开的flag(方式),第三个参数是回调函数,即如果打开错误,返回输出错误,否则输出成功打开文件。

其中flag有下面的方式: 

  • r 读模式打开文件
  • r+  读写模式打开文件
  • rs 同步方式打开文件
  • rs+ 同步方式打开和读写文件
  • w  写入方式打开文件
  • w+ 读写方式打开文件,如果没有,就创建
  • 。。。

 

获取文件的相关信息

举例如下:

输入如下所示:

nodejs基础

var fs = require("fs");
fs.stat("foo.txt", function (err, stats) {
    if (err) {
        return console.log(err);
    }
    console.log(stats);
    console.log(stats.isFile());
    console.log(stats.isDirectory());

});

nodejs基础

 

输出如下所示:

nodejs基础

$ node inherits
{ dev: 917962,
  mode: 33206,
  nlink: 1,
  uid: 0,
  gid: 0,
  rdev: 0,
  blksize: undefined,
  ino: 3377699720545815,
  size: 60,
  blocks: undefined,
  atime: 2017-04-12T12:30:03.350Z,
  mtime: 2017-04-12T12:30:33.144Z,
  ctime: 2017-04-12T12:30:33.144Z,
  birthtime: 2017-04-12T12:30:03.228Z }
true
false

nodejs基础

 

可以看出fs.stat()接受两个参数,第一个是文件名,第二个是一个回调函数,回调函数中有两个参数 ,第一个参数是err,即发生错误时我们return,并且console.log(err),所以最终是return console.log(err); 第二个参数

是stats,实际上是fs.stats对象,这个对象中包含了很多关于这个文件的详细信息---包括uid、gid、size、birthtime等。

 

 

写入文件

nodejs基础

var fs = require("fs");
fs.writeFile("input.txt", "somethingsomethingsomething", function (err) {
    if (err) {
        return console.log(err);
    }
    console.log("seccess!");
    fs.readFile("input.txt", function (err, data) {
        if (err) {
            return console.log(err);
        }
        console.log(data.toString());
    });
});

nodejs基础

 

 即这里首先引入了fs,然后写文件函数接受三个参数,第一个是将要写的文件(如果之前没有,就创建之后再写入),第二个是要写入的内容, 第三个是一个匿名函数,如果出错,就返回错误,否则,读取文件,其中函数的第二个参数data即文件的内容,利用toString()就能还原。

 

 

创建目录

nodejs基础

var fs = require("fs");
fs.mkdir("./new", function (err) {
    if (err) {
        return console.log(err);
    }
    console.log("seccess!");
});

nodejs基础

 

 这样,就可以在当前目录下创建一个new文件夹了。 注意: 最好使用相对路径。 

 

读取目录

nodejs基础

var fs = require("fs");
fs.readdir("./some", function (err, files) {
    if (err) {
        return console.log(err);
    }
    files.forEach(function (file) {
        console.log(file);
    });
});

nodejs基础

 

注意:在当前的some文件夹下有两个txt文件,我们只要给readdir()的第二个参数传入一个函数,第二个参数是files(即文件夹中的所有文件),就可以通过forEach来遍历输出了。结果如下:

df.txt
g.txt

 

 这里回顾一下forEach的用法:

 var arr = [1,2,3,4,5,6,7,8,9];
 arr.forEach(function (value) {
      console.log(value);
 });

 

即forEach用于数组,它接受一个函数作为参数,函数的参数就是要遍历的数组中的每一个值,最终的结果如下:

nodejs基础

 

删除目录

nodejs基础

var fs = require("fs");
fs.rmdir("./ha", function (err) {
    if (err) {
        return console.log(err);
    }
    console.log("success");
});

nodejs基础

 

注意:删除目录时,只能删除文件夹中没有文件的文件夹,否则无法删除成功。那么怎么删除文件呢?如下:

 

 

删除文件

nodejs基础

var fs = require("fs");
fs.unlink("./foo.txt", function (err) {
    if (err) {
        return console.log(err);
    }
    console.log("seccess!");
});

nodejs基础

 

这样就可以删除掉当前页面下的foo.txt文件。 即unlink,不再相关的意思。如果希望删除其他文件夹下的文件也非常简单,如下:

nodejs基础

var fs = require("fs");
fs.unlink("./some/g.txt", function (err) {
    if (err) {
        return console.log(err);
    }
    console.log("seccess!");
});

nodejs基础

 

 即修改相对路径即可完成。

 

关闭文件

fd---file descriptor 文件描述符

nodejs基础

var fs = require("fs");
var buf = new Buffer(1024);

console.log("准备打开文件!");
fs.open('input.txt', 'r+', function(err, fd) {
   if (err) {
       return console.error(err);
   }
   console.log("文件打开成功!");
   console.log("准备读取文件!");
   fs.read(fd, buf, 0, buf.length, 0, function(err, bytes){
      if (err){
         console.log(err);
      }

      // 仅输出读取的字节
      if(bytes > 0){
         console.log(buf.slice(0, bytes).toString());
      }

      // 关闭文件
      fs.close(fd, function(err){
         if (err){
            console.log(err);
         } 
         console.log("文件关闭成功");
      });
   });
});

nodejs基础

 

其中input.txt中是有内容的,重要的是在open方法的参数中,第三个参数的第二个是fd---文件描述符,即必须通过文件描述符来read和close。

 

 

16. nodejs GET/POST 请求(重点)

nodejs是服务器端语言,当然要和浏览器打交道,所以处理get、post请求是非常必要的。

获取get请求内容

nodejs基础

var http = require("http");
var util = require("util");
var url = require("url");
http.createServer(function (req, res) {
    res.writeHead(200,{"Content-Type":"text/plain"});
    res.end(util.inspect(url.parse(req.url)));
    console.log(url.parse(req.url).path);
}).listen(3000);

nodejs基础

 

 这里引入了util模块用于inspect,引入了url模块用于parseGET请求的url。 注意我们再parse之后得到的是一个对象,这个对象中包含了req.url的详细信息。浏览器效果如下:

nodejs基础

即nodejs就相当于运行了本地服务器,我们使用localhost:3000即表示访问我们本地服务器的3000端口。和127.0.0.1:3000的效果是一样的。

nodejs基础

var http = require("http");
var url = require("url");
http.createServer(function (req, res) {
    res.writeHead(200,{"Content-Type":"text/plain"});
    var params = url.parse(req.url, true).query;
    res.write(params.name+"\n");
    res.write(params.age);
    res.end();
}).listen(3000);

nodejs基础

 

最终的结果如下:

nodejs基础

 

获取POST请求内容: 

使用post请求后,需要我们使用nodejs在后台利用req.on和querystring.pase来手动解析post请求。

一个含有post请求的页面如下:

nodejs基础

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>for_post</title>
</head>
<body>
    <form method="post">
        name: <input type="text" name="name"> <br>       
        age: <input type="text" name="age"> <br><br>
        <input type="submit">
    </form>
</body>
</html>

nodejs基础

 

nodejs的文件如下:

nodejs基础

var http = require("http");
var fs = require("fs");
var querystring = require("querystring");

http.createServer(function (req, res) {
    var body = "";
    req.on("data", function (chunk) {
        body += chunk;
    });
    req.on("end", function () {
        body = querystring.parse(body);
        res.writeHead(200, {'Content-Type': 'text/html; charset=utf8'});
        if (body.name && body.age) {
            console.log("true");
            res.write("The client is " + body.name);
       res.write("<br>");
            res.write("This clent is " + body.age + " years old");
            res.end();
        } else {
            console.log("else");
            fs.readFile("./post.html", function (err, data) {
                if (err) {
                    return console.log(err);
                }
                res.write(data);
                res.end();
            });
        }
    });

}).listen(8888);

nodejs基础

 

最终的效果如下:

nodejs基础

good!  这个也很容易嘛!

 

 

17. nodejs工具模块

 nodejs中提供了很多好用的模块,如下:

OS模块 --- 提供基本的系统操作函数

 Path模块 --- 提供了处理和转换文件的工具

Net模块 ---  用于底层的网络通信,提供了网络端和服务器端的模块

Dns模块 --- 用于解析域名

Domain模块 --- 简化异步代码的异步处理,可以捕捉try-catch无法捕捉的。

 

OS模块

nodejs基础

var os = require("os");

// 操作系统的默认临时文件夹
console.log(os.tmpdir()); //C:\Users\ADMINI~1\AppData\Local\Temp

// 操作系统的字节序
console.log(os.endianness()); // LE

// 操作系统的主机名
console.log(os.hostname()); //718JVP8AUMCTA6H 

// 操作系统名
console.log(os.type()); // window_NT 注意XP等系统都是基于window_NT发展的

// 操作系统
console.log(os.platform()); // win32

// 操作系统的发行版本
console.log(os.release()); // 10.0.14393  我的系统是win10的

// 系统内存总量
console.log(os.totalmem()); // 8475926528

// 系统的空闲内存
console.log(os.freemem()); // 4304961536

// CPU的信息
console.log(os.cpus()); 

nodejs基础

 

CPU信息如下:
nodejs基础

即四核的CPU,基本相同,urse略有差别 。

 

Path()模块

这个模块主要给了一些处理路径的api,如规范化路径、拼接路径、返回后缀名等,举例如下:

var path = require("path");
console.log("normalization:" + path.normalize('/test/test1//test2..'));
console.log("joinPath:" + path.join('test', 'test1/', '/test2'));
console.log("toAbsolute:" + path.resolve("main.js"));
console.log("extName:" + path.extname("main.js"));

 

结果如下所示:

normalization:\test\test1\test2..
joinPath:test\test1\test2
toAbsolute:C:\Users\Administrator\Desktop\node\main.js
extName:.js

 

 

 Net()模块

 这里的内容非常多,等需要的时候再深入学习。

 

 

Dns模块

dns模块中也有不少内容,但是还不知道具体的作用,下面的例子应该是常用的:

nodejs基础

var dns = require("dns");
dns.lookup("www.baidu.com", function onLookup(err, address) {
    if (err) {
        return console.log(err);
    }
    console.log(address);
});

nodejs基础

 

这样,就可以解析出www.baidu.com的域名,通过address即可输出119.75.217.109

 

 

Domain模块

这个模块主要用于简化异常处理。

 

 

18. nodejs Web 模块

  使用node创建web服务器端

  大多数web服务器都支持服务器端脚本语言,如ruby、python、php等,并通过脚本语言,从数据库获取数据,将结果返回给客户端浏览器, 目前主流的服务器是Apache、Nginx和IIS。

  nodejs基础

 当然,这里讲的是node, 使用node就可以创建一个服务器。即引入http模块创建。

 下面的演示是一个最基本的服务器架构:

nodejs基础

var http = require("http");
var fs = require("fs");
var url = require("url");

http.createServer(function (req, res) {
    var path = url.parse(req.url).pathname;
    fs.readFile(path.substr(1), function (err, data) {
        if (err) {
            console.log(err);
            res.writeHead(404, {"Content-Type": "text/html"});
        } else {
            res.writeHead(200, {"Content-Type": "text/html"});
            res.write(data.toString());
            res.end();
        }
    });
}).listen(8081);

console.log("Server running at http:127.0.0.1:8081");

nodejs基础

 演示如下:

nodejs基础

 

  使用node创建web客户端

 注意:服务端还用之前的server.js,下面为client.js

nodejs基础

var http = require("http");
var options = {
    host: "localhost",
    port: "8081",
    path: "/index.html"
};
var callback = function (res) {
    var body = "";
    res.on("data", function (data) {
        body += data;
    });
    res.on("end", function () {
        console.log(body);
    });
};
var req = http.request(options, callback);
req.end();

nodejs基础

 

  首先在终端运行server.js,然后再打开一个终端,运行client.js,可以发现输出如下:

nodejs基础

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>index</title>
    <style>
        h1 {
            color: red;
        }
    </style>
</head>
<body>
    <h1>hello, this is the index.html.</h1>
</body>
</html>

 

 

19. nodejs Express框架

jQuery是客户端js的框架,同样Express是服务器端nodejs的框架,它提供了一系列的强大特性帮助我们快速构建web应用,也提供了丰富的http工具。使用Express可以快速搭建一个功能完整的网站。

Express的核心特性是:

  • 设置中间件来响应http请求。
  • 制定路由表,来响应不同的http请求操作。
  • 通过向模板传递参数,动态渲染html。

更多内容看《nodejs 之 Express 框架》。

 

20. nodejs RESTful API

 REST 即表述性状态转移, Representional State Transfer, 这是一种软件架构风格。表述性状态转移是一组架构约束条件和原则。满足这些条件的程序就是RESTful,或者说基于REST架构的web service就是RESTful。

创建RESTful

 

 

 21. nodejs 多进程

引入child_process模块就可以使用它的api来创建多进程。