nodeJS(之一)Stream

| createReadStream() - 创建可读流
| createWriteStream() - 创建可写流
| setEncodeing() - 设置编码
|

Stream

Stream把较大的数据,拆分成小的部分

读取文件的两种: fs.readFile()和fs.createReadStream()

stream提供了以下四种类型的流:

    var Stream = require('stream');

    var Readable = Stream.Readable;
    var Writable = Stream.Writable;
    var Duplex = Stream.Duplex;
    var Transform = Stream.Transform;

createReadStream()、createWriteStream()

// 创建一个Readable对象以读取bigFile内容
// 如果使用fs.readFile()可以会由于文件过大而失败
fs.createReadStream(bigFile);

setEncodeing() 设置编码

var readerStream = fs.createReadStream('input.txt');

readerStream.setEncodeing('UTF8');

事件

可读数据流的事件

    readable 数据向外流时触发

    data 对于那些没有显式暂停的数据流,添加data事件监听函数,会将数据流切换到流动态,尽快向外提供数据

    end 读取完数据时触发。注意不能和 writeableStream.end() 混淆,writeableStream 并没有 end 事件,只有 .end() 方法

    close 数据源关闭时触发

    error 读取数据发生错误时触发


可写数据流的事件

    drain writable.write(chunk) 返回 false 之后,缓存全部写入完成,可以重新写入时就会触发

    finish 调用 .end 方法时,所有缓存的数据释放后触发,类似于可读数据流中的 end 事件,表示写入过程结束

    pipe 作为 pipe 目标时触发

    unpipe 作为 unpipe 目标时触发

    error 写入数据发生错误时触发

| https://cnodejs.org/topic/570b1fa494b38dcb3c09a7f8
| http://fe.meituan.com/stream-basics.html

nodeJS (之三) Buffer

| Buffer.from() - 创建buffer // 老方法 new Buffer(str, encode);
| toString() - Buffer转字符串
| length - 返回Buffer的长度
| concat([buf1, buf2], 输出的长度) - 合并
| slice(start, end) - 截取
| copy(buffer, 开始目标位置, start, end) - 拷贝
| compare() - 比较两个buffer的大小
| fill() - 用指定的值填充Buffer
| indexOf(val) - 返回指定字符所在Buffer的位置
| lastIndexOf() - 从后向前查找
| includes(val, begin, uncode) - 查找是否存在
| keys() - 获取
| write() - 向指定的buffer写入内容
|
| Buffer.isBuffer(buf) - 是否是一个buffer
| Buffer.isEncoding(‘utf-8’) - 检测是否是一个有效的编码
| Buffer.byteLength - 判断字符的长度

Buffer

Buffer用于创建存放和处理二进制数据的缓存区,buffer是全局不需要require('buffer')

网络上发送和接收经常是以二进制传输数据:

    - 通过TCP连接发送和接收数据;
    - 从图像或者压缩文件读取二进制数据;
    - 从文件系统读写数据;
    - 处理来自网络的二进制数据流


1、字节byte: 1个字节等于8位二进制

       位bit: 每位存储0或1,每8位等于一个字节,最大0-255字节

       一个汉字三个字节,其它两个字节


2、Node.js 目前支持的字符编码包括: 

    'ascii' - 仅支持 7 位 ASCII 数据。如果设置去掉高位的话,这种编码是非常快的。

    'utf8' - 多字节编码的 Unicode 字符。许多网页和其他文档格式都使用 UTF-8 。

    'utf16le' - 2 或 4 个字节,小字节序编码的 Unicode 字符。支持代理对(U+10000 至 U+10FFFF)。

    'ucs2' - 'utf16le' 的别名。

    'base64' - Base64 编码。当从字符串创建 Buffer 时,按照 RFC4648 第 5 章的规定,这种编码也将正确地接受“URL 与文件名安全字母表”。

    'latin1' - 一种把 Buffer 编码成一字节编码的字符串的方式(由 IANA 定义在 RFC1345 第 63 页,用作 Latin-1 补充块与 C0/C1 控制码)。

    'binary' - 'latin1' 的别名。

    'hex' - 将每个字节编码为两个十六进制字符。

new Buffer

new Buffer(size): 创建一个Buffer对象, 并返回一个数组

Buffer创建的三种类型:

    1、new Buffer(2)            // 创建buffer的长度

        let buf = new Buffer(2);
        buf[1] = '232';
        buf[2] = '2312';
        buf[3] = 'asasdf';
        console.log('输出buffer: ', buf);        // 输出buffer:  <Buffer 00 e8>  只能输出两个字节

    2、new Buffer(数组)            // new Buffer([1,3,5])

    3、new Buffer(字符串)        // new Buffer('我')

字符串与buffer互转

1、字符串转buffer

    let buf = new Buffer('sss')
    console.log(buf);            // <Buffer 73 73 73>


2、toString(编码, 获取开始位置 , 获取结束位置): buffer转换成字符串

    let buf = Buffer.from('sss');
    console.log(buf);            // Buffer e7 be 8e e5 a5 bd  输出的6个字符
    console.log(buf.length);    // 6 buffer的长度
    console.log('输出buffer: ', buf.toString('utf-8', 3, 6));    // 输出好     

length 获取长度

let buf = new Buffer('sssss');
console.log(buf.length);     // 5

concat([ 要合并的数据集合 ], 要输出的长度): 合并

let buf1 = new Buffer('忍');
let buf2 = new Buffer('者');
let buf3 = new Buffer('神');
let buf4 = new Buffer('龟');

let newBuf = Buffer.concat([buf1, buf2, buf3, buf4]);
console.log(newBuf);                // 12字节
console.log(newBuf.toString());     // 输出忍者神龟

slice(start, end): 截取

let buf = new Buffer('怎么来截取buffer中的字符');
console.log(buf.slice(9, 15).toString());        // 返回 截取

copy(buffer, 开始目标位置, start, end): 拷贝

let buf = new Buffer('怎么来截取buffer中的字符');
let copyBuf = new Buffer(9);
buf.copy(copyBuf, 6, 0, 9);     // 从buf的第六个字节开始,0到9字符copy

console.log(copyBuf.toString());        // 输出 来截取

Buffer.byteLength: 判断字符的长度

toString() Buffer转字符串

toString([字符编码], [开始转换位置], [结束转换位置])

let buf = new Buffer('哈哈哈');
console.log(buf.toString('utf8'));        // urt8输出为 哈哈哈

可以转换成:  ascii、utf8、utf16le、ucs2、base64

compare() 比较buffer

返回比较结果: 0 两个buffer相等, -1 前者小于后者, 1 前者大于后者

const bufA = new Buffer('123345');
const bufB = new Buffer('123345aaaaa');
const result = bufA.compare(bufB);

switch(result){
    case -1: 
        console.log(`bufA 小于 bufB`);
        break;

    case 0: 
        console.log(`bufA 等于 bufB`);
        break;

    case 1: 
        console.log(`bufA 大于 bufB`);
        break;

    default: break;
}

fill() 用指定的值来填充Buffer

fill(val,[offset], [end], [encode]): 用指定的值来填充Buffer

Example:
    let buf = new Buffer(5);    
    buf.fill('#');
    console.log(buf);

/** 查找与搜索 */

indexOf() 返回指定字符所在Buffer的位置

let buf = new Buffer('aabbbccc');
console.log(buf.indexOf('b'));      // 返回2 如果不存在返回-1

lastIndexOf() 从尾查找所在的位置

includes(val) 返回指定字符是否存在

includes(搜索的值, [buf开始搜索的位置], [搜索的编码])

Example:
    let buf = new Buffer('aaabbbcccddd');
    console.log(buf.includes('b', 2));     // true 或  false

keys() 把buffer中的数组下标

const buf = new Buffer('buffer');
console.log(buf)

for(const key of buf.keys()){
    console.log(key);            // 0、1、2、3、4、5
}

write() 将值写入到buffer的指定位置

write(string, [写入的位置], [写入的字节], [字符编码])

Buffer.isBuffer() 判断是否是Buffer

let str = '';
let buf = new Buffer(10);
console.log(Buffer.isBuffer(str));      // false
console.log(Buffer.isBuffer(buf));      // true

Buffer.isEncoding(endcode): 判断编码

nodeJS(之六)Querystring

parse(str, [sep], [eq], [options]) 解析url的查询字符串

var param = 'user=siguang&age=30';
qs.parse(param).user;    // 'siguang'

stringify(obj, [sep], [eq]) 将对象转成url的参数字符串

const obj = {name: '一介布衣', url: 'http://yijiebuyi.com'};
const param = querystring.stringify(obj);
console.log(param);            // name=%E4%B8%80%E4%BB%8B%E5%B8%83%E8%A1%A3&url=http%3A%2F%2Fyijiebuyi.com

qs.stringify({foo: 'bar', baz: 'qux'}, ';', ':')
// 返回 'foo:bar;baz:qux'

escape(str) 字符进行编码

let url = 'https://github.com/siguang1983';
console.log(qs.escape(url));        // https%3A%2F%2Fgithub.com%2Fsiguang1983

unescape(str) 字符进行解码

let code = 'https%3A%2F%2Fgithub.com%2Fsiguang1983';
console.log(qs.unescape(url));        // https://github.com/siguang1983

nodeJS(之七)URL

| URL 用于URL的处理和解析
| const url = require(‘url’) // 引用

href 解析后的完整url

'http://user:pass@host.com:8080/p/a/t/h?query=string#hash'

protocol 返回协议 ‘http’

host 返回主机和端口

auth 返回 URL 的用户名与密码部分

hostname 主机

port 端口

pathname URL的整个路径部分

search 获取url参数 ‘?query=string’ 包括?号

path 返回url和参数不包括协议和主机 ‘/p/a/t/h?query=string’

query 返回参数对象返回 ‘query=string’ or {‘query’: ‘string’}

hash 返回hash部分 ‘#’

url.format(urlObject) 根据对象构建一个路径

var obj = { 
    protocol: 'https',
    host: 'www.sss.com:4000',
    pathname: 'index' 
}
url.format(obj)
//returns 'https://www.sss.com:4000/index'

parse(urlString[, parseQueryString[, slashesDenoteHost]]) 返回url的一些信息, querystring为true使用querstring模块来解析url中的查询字符

exports.createServer2 = function(){
    var server = http.createServer(function(request, response){
        var oUrl = url.parse(request.url);
        console.log('路径', oUrl);               
    });

    server.listen(8081, 'localhost');
}

返回:
{
    protocol: null,            // 协议 http
    slashes: null,
    auth: null,                // 身份
    host: null,                // 主机    
    port: null,                // 端口
    hostname: null,            // 主机名
    hash: null,
    search: null,
    query: null,            // 查询参数或者使用querystring.parse() 返回{username:'siguang'};
    pathname: '/apples/',    // 路径名
    path: '/apples/',        // 路由
    href: '/apples/'         // 链接
}

resolve(from, to) 两个网址拼接

url.resolve('http://example.com/', '/one')    // 'http://example.com/one'
url.resolve('http://example.com/one', '/two') // 'http://example.com/two'

slashes

nodeJS(之十)TCP

TCP

NodeJS中有三种socket: 1、TCP   2、UDP   3、Unix域套接字

TPC套接字分为: 服务端和客户端

    服务端TCP监听来自客户端的连接请求,并使用TCP连接向客户端发送数据;客户端TCP连接到服务端并与服务器交互数据。客户端与服务端之间依靠套接字进行双向通信。

1、引用TCP模块

    const net = require('net');
    const PROT = 6969;

2、HTTP模块也是继承TCP模块

net.Server类

1、createServer() 创建tcp服务器

2、close() - 关闭

3、listen() - 侦听

    const HOST = '127.0.0.1';
    const PORT = 6969;

    // 写法1 
    let server = net.crateServer(function(conn){
        ...
    });
    server.listen({host: HOST, port: PORT}, function(){    // 或 server.listen(PORT, HOST, function(){
        console.log('这里是异步的');
    });

    // 写法2
    let server = net.crateServer(function(conn){
        ...
    }).listen(PORT, HOST);


4、address() - 返回绑定的IP端口等信息 {"address":"::","family":"IPv6","port":6969}

属性

1、listening - server是否正在监听连接

2、maxConnections - 当前server连接数过多时拒绝连接


事件

1、close - 关闭

2、connection - 建立连接时触发

3、error - 错误时触发

4、listening - 服务被绑定后调用

net.Socket类

net.connect

nodeJS(之九)Connect中间件

Connect

Connect是HTTP服务器的中间件

$ 安装 npm i connect --save

// 引用模块
var connect = require('connect');

// 创建服务器
var server = connect.createServer();

// 处理静态文件
server.use(connect.static(__dirname + '/statice'));     // connect.static 静态文件目录

// 监听
server.listen(3000);

方法

1、静态目录设置 -  server.use(connect.static(__dirname + '/statice'));

    server.use(connect.static('my-images', __dirname + '/statice'));        // 将my-images指向到

2、客户端缓存时间 -  server.use(connect.static(__dirname + '/statice'), {maxAge: 100000});

3、静态文件以"."开始都认为是隐藏文件 - server.use(connect.static(__dirname + '/statice'), {hidden: true});

4、query中间件,解析字符串  '/posts?page=5'

    server.use(connect.query);
    server.use(function(req, res){
        let page = req.query.page;
    })

5、logger,打印日志,四种日志格式,default/dev/short/tiny

    connect.createServer(
        connect.logger('dev'),
        function(req, res){
            res.writeHead(200);
            res.end('hello);
        }
    )

7、body parser  文件上传
使用cookieParser()

// cookie: secret1=val1; secret2=val2

server.use(cookieParser());
server.use(function(req, res){
        let ser1 = req.cookies.secret1;
        let ser2 = req.cookies.secret2;
})

session 会话

nodeJS基 (之一) Global全局对象

| filename - 前当文件名
|
dirname - 当前所在的目录
| setTimeout、clearTimeout - 定时器
| setInterval、clearInterval - 定时器
| console - 打印到控制台
| process - 进程
| exports、require() - 导入、导出
|

__dirname 当前的绝对路径

__filename 当前文件名

exports 暴露接口

require() 引入模板

1、模块分为

    模块加载:require('2.js');

    1)内置模块:  var fs = require('fs');        // 加载Node的内置模块 fs文件系统

    2)文件模块: 自己定义的业务模块

        定义一个food.common.js

        var food = reuqire('./js/food.common');        // 加载业务模块

    3)加载第三方模块

        require('./2');        // 先按照加载的模块文件进行查找,如果没有找到会在文件名加上.js


2、绝对和相对路径

    var fs = require('fs');        // 绝对路径,是在Node通过内部的node_modules查找到的模块

    var moduleA = reuqire('./lib/moduleA');        // 相对路径


2、npm 全局安装和局部安装

    1)全局安装会安装到Node目录中,各项目都可以使用 npm install -g gulp

    2)局部安装,将一个模块安装到node_modules中,只在当前和子目录中使用


require.cache
require.resolve()

global 全局对象

module

module和exports两个全局变量

1)module:每个模块下都包括module对象

    Module {
        id: '.',
        exports: {},
        parent: null,
        filename: '/Users/apple/siguang.liu/nodeProject/server.js',
        loaded: false,
        children: [],
        paths: [
            '/Users/apple/siguang.liu/nodeProject/node_modules',
            '/Users/apple/siguang.liu/node_modules',
            '/Users/apple/node_modules',
            '/Users/node_modules',
            '/node_modules'
        ]
     }

 2)exports外部接口,通过require就可以调用这个接口

     其实exports就是module.exports的引用

    exports.name = 'siguang';
    exports.getPrivate = function(){
        return 'haha';
    }

process 进程

console 打印到控制台

log()、info()、error()、warn()、time()、timeEnd()

setImmediate(callback[, …args])、clearImmediate(immediateObject)

setInterval()、clearInterval()

setTimeout()、clearTimeout()

nodeJS基 (之五) Path模块

| baseName() - 返回文件名,带扩展
| dirname() - 返回一个指定的绝对路径
| join() - 两个路径进行拼接
| extname() - 返回文件的扩展名
| isAbsolute() - 是否是一个绝对路径
| resolve() - 把路径解析成一个绝对路径
| format() - 从配置中返回一个路径
| parse() - 拆分一个路径,与format相反

baseName(): 返回文件名(带扩展名)

let name = path.basename('./resource/haha.txt', '.txt');    // 返回 haha
let allName = path.basename('./resource/haha.txt');         // 返回 haha.txt

dirname() 返回一个path目录

let pathName = path.dirname('./');
console.log(pathName, __dirname);

extname(fileStr) 返回文件的扩展名

let extension = path.extname('权限申请.xlsx');
console.log(extension);        // .xlsx

isAbsolute(path) 判断path是否是一个绝对路径

var absolutePath =  path.isAbsolute('./ss');
var notAbsolutePath = path.isAbsolute('/Users/apple/')
console.log(absolutePath);      // false
console.log(notAbsolutePath);   // true

join(paths) 多个路径拼接成一个路径

let pathStr = path.join('/foo', 'bar', 'bar/asdf', 'quux');
let parentDirectory = path.join('/foo', 'bar', 'bar/asdf', 'quux', '..');    // ..减少一级目录
console.log(pathStr);           // /foo/bar/bar/asdf/quux
console.log(parentDirectory);   // /foo/bar/bar/asdf

resolve(p1, p2) 把路径解析为一个绝对路径

var newPath = path.resolve(__dirname, 'sss.txt');
console.log(newPath);

format(pathobject) 从配置对象返回一个路径字符串

let basePath = path.format({
    root: '/book',
    dir: '/home/user/dir',
    base: 'file.txt'
})
console.log(basePath);      // output ->  /home/user/dir/file.txt

parse(path) 将一个路径拆分与format相反

let pathStr = path.parse(__dirname);
console.log(pathStr);

nodeJS(之八)HTTP

HTTP超文本传输协议

let http = require('http');        // 导入http模块

HTTP协议构建就是在请求和响应上,由http.ServerRequest和http.ServerResponse构造出来的

http.createServer(function(req, res){
    if(req.url === '/login'){
        res.writeHead(200, {'Content-Type': 'text/html' });
        res.end('<div>login</div>');
    }
    else if(req.url === 'main' && req.method === 'POST'){
        res.writeHead(200, {'Content-Type': 'text/html' });
        res.end('Hello <b>post</b>');
    }
}).listen(3030)

http.server类

是一个创建服务的类 ServerRequest()

方法:

    1、close([callback]): 停止服务器连接

    2、listen(path, [callback]): 侦听, 与TCP使用方法相同

属性:

    1、listening: 返回是否在监听连接服务器的布尔值

    2、maxHeadersCount: 限制最大的请求头数据,默认2000

    3、keepAliveTimeout

    3、setTimeout(msecs, callback): 为socket设置超时值,如果一个超时发生

事件

    1、connect - 请求时触发


Example:

    const HOST = '127.0.0.1';
    const PORT = 3000;

    // 创建服务器
    let server = http.createServer((req, res) => {
        console.log('请求方式:', request.method);       // 请求方式
        console.log('请求url:', request.url);          // 请求url
        console.log('请求头:', request.headers);       // 请求头

        res.writeHead(200);
        res.end('hello world');    
    })            

    // 侦听3000端口
    server.listen(PORT, HOST, ()=> {
        console.log(`server running at http://${hostname}:${port}`);
    })

Request类

url - 返回url地址

method - 返回请求方法

headers - 请求头

Response类

// 这里res返回的就是ServerResponse类
const server = http.createServer((req, res) => { ..... });   

方法:

1、addTrailers(headers)

2、end([data],[encoding],[callback]) - 告诉服务器所有响应头和主体都已被发送,等同于response.write()

3、finished: 

4、getHeader(name): 

5、headersSent: 返回消息头是否被发送,发送为true否则false

6、removeHeader(name): 移出一个头消息 response.removeHeader('Content-Encoding');

7、sendDate: 如果设置为true消息头存在日期消息头则自动生成并且响应在发送

8、setHeader(name, value): 设置头信息

    response.setHeader('Content-Type', 'text/html');
    response.setHeader('Set-Cookie', ['type=ninja', 'language=javascript']);

9、setTimeout(msecs, callback): 设置 socket 的超时时间

10、statusCode: 设置向客户端响应的状态码  res.statusCode = 200;

11、statusMessage

12、write(chunk[, encoding][, callback])

13、writeContinue()

14、writeHead(statusCode[, statusMessage][, headers]): 发送一个响应头给请求

    writeHead(状态码, [状态消息], {响应头信息})

    Example: res.writeHead(200, { content-Type: 'text/html', Connection: 'keep-alive'})

15、setEncoding(编码): 设置编码格式 res.setEncoding('utf8');


res.connection

事件:

    1、close - 关闭连接,会自动调用response.end();

    2、finish - 当响应已被发送时触发

http.IncomingMessage 类

const server = http.createServer((req, res) => { ..... });  就是req返回的类

1、headers: 客户端的请求头

2、method: 获取请求方式

3、url: 请求url

4、statusCode: 状态码

5、statusMessage: 状态消息

6、socket: 与连接有关的 net.Socket 对象。

7、destroy([error])

8、httpVersion: http的版本

9、rawHeaders

10、rawTrailers

11、setTimeout(msecs, callback)

12、trailers

http.createServer(): 创建server服务

http.get(options, [callback]): 用来模拟客户端向服务器发送请求

Example: 请求nodejs.org的index.json文件,返回json的内容

const http = require('http');

http.get('http://nodejs.org/dist/index.json', (res) => {
    const statusCode = res.statusCode;
    const contentType = res.headers['content-type'];

    let error;
    if (statusCode !== 200) {
        error = new Error(`请求失败。\n` + `状态码: ${statusCode}`);
    } else if (!/^application\/json/.test(contentType)) {
        error = new Error(`无效的 content-type.\n` + `期望 application/json 但获取的是 ${contentType}`);
    }
    if (error) {
        console.log(error.message);
        // 消耗响应数据以释放内存
        res.resume();
        return;
    }

    res.setEncoding('utf8');
    let rawData = '';
    res.on('data', (chunk) => rawData += chunk);
    res.on('end', () => {
        try {
            let parsedData = JSON.parse(rawData);
            console.log(parsedData);
        } catch (e) {
            console.log(e.message);
        }
    });
}).on('error', (e) => {
    console.log(`错误: ${e.message}`);
});

http.request(options, [callback]): 模拟HTTP请求

options:

    protocol <String> 使用的协议。默认为 'http:'。

    host <String> 请求发送至的服务器的域名或 IP 地址。默认为 'localhost'。

    hostname <String> host 的别名。为了支持 url.parse(),hostname 优于 host。

    family <Number> 当解析 host 和 hostname 时使用的 IP 地址族。 有效值是 4 或 6。当未指定时,则同时使用 IP v4 和 v6。

    port <Number> 远程服务器的端口。默认为 80。

    localAddress <String> 要绑定到网络连接的本地接口。

    socketPath <String> Unix 域 Socket(使用 host:port 或 socketPath 的其中之一)。

    method <String> 一个指定 HTTP 请求方法的字符串。默认为 'GET'。

    path <String> 请求的路径。默认为 '/'。 应包括查询字符串(如有的话)。如 '/index.html?page=12'。 
    当请求的路径中包含非法字符时,会抛出异常。 目前只有空字符会被拒绝,但未来可能会变化。

    headers <Object> 一个包含请求头的对象。

    auth <String> 基本身份验证,如 'user:password' 来计算 Authorization 头。

    agent <http.Agent> | <Boolean> 控制 Agent 的行为。 当使用 Agent 是,请求默认为 Connection: keep-alive。 可能的值有:
    undefined (默认): 对该主机和端口使用 http.globalAgent。

    Agent 对象:显式地使用传入的 Agent。

    false: 不对连接池使用 Agent,默认请求 Connection: close。

    createConnection <Function> 当不使用 agent 选项时,产生一个用于请求的 socket/stream 的函数。 这可以用于避免创建一个自定义的 Agent 类,只是为了覆盖默认的 createConnection 函数。详见 agent.createConnection()。

    timeout <Integer>: 一个数值,指定 socket 超时的毫秒数。 它会在 socket 被连接时设置超时。


Example:

    var postData = querystring.stringify({
        'msg' : 'Hello World!'
    });

    var options = {
        hostname: 'www.google.com',
        port: 80,
        path: '/upload',
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
            'Content-Length': Buffer.byteLength(postData)
        }
    };

    var req = http.request(options, (res) => {
        console.log(`STATUS: ${res.statusCode}`);
        console.log(`HEADERS: ${JSON.stringify(res.headers)}`);
        res.setEncoding('utf8');
        res.on('data', (chunk) => {
            console.log(`主体: ${chunk}`);
        });
        res.on('end', () => {
            console.log('响应中已无数据。');
        });
    });

    req.on('error', (e) => {
        console.log(`请求遇到问题: ${e.message}`);
    });

    // 写入数据到请求主体
    req.write(postData);
    req.end();

http概念

1、请求方法:

    Get: 请求从服务器获取数据,没有请求体,不会影响服务器数据

    Post: 从服务器获取数据,有请求体,会影响服务器端的数据

    DELETE: 从服务器删除资源

    HEAD: 向服务器获取响应头,不要响应体

    PUT: 要新的服务器一个资源

    OPTIONS: 获取服务器支持的方法


2、文件类型 Content-type

    html: text/html

    ASCII文本: text/plain

    jpeg图片: image/jpeg

    GIF图片: image/gif

    javascript: text/javascript

    css: text/css

    表单: application/x-www-form-urlencoded



# 获取 index.html 文件内容返回给客户端

    var http = require('http');
    var fs = require('fs');

    exports.createServer2 = function(){
        var server = http.createServer(function(request, response){
        var url = request.url;

            // 获取 index.html 文件内容返回给客户端
            fs.readFile('./index.html', 'utf-8', function(err, data){
                console.log(err, data);
                response.write(data);
                response.end();
            })
        });
        server.listen(8081, 'localhost');
    }


# 处理Get请求的参数

    var server = http.createServer(function(request, response){
            var oUrl = url.parse(request.url, true);

            console.log(oUrl.query);        // { username: 'siguang', age: '30' }

            var urlQuery = oUrl.query;
            if(urlQuery){
                    response.write('服务器接收GET请求参数成功\n')
                    response.write('用户名:'+ urlQuery.username +'\n');
                    response.write('年级:'+ urlQuery.age +'\n');
            }
            else{
                    response.write('服务器接收参数失败');
            }
            response.end();
    });

    server.listen(8081, 'localhost');


# 处理POST请求

    if(urlObj.pathname == '/register'){           // 处理 '/clock' 的请求
        var str = '';
        var userList = {};

        // 通过on来侦听 data事件, data事件就是接收客户端传输过来的事件, 用可接收post的数据
        request.on('data', function(data){
            console.log(data.toString());          // 返回的是 Buffer
            str += data.toString();
        })

        // 所有接收完成后处理接收数据
        request.on('end', function(data){
            userList = JSON.parse(str);

            response.writeHead('200', {'Content-Type': 'text/html;charset=utf-8'});
            if(userList.username == 'aaa' && userList.password == 'ssssss'){
                    response.end(JSON.stringify({"res":true,"message":"注册成功"}));        // 这里一定要用JSON.stringify来转成串
            }
            else{
                response.end(JSON.stringify({'res':false,'message':'注册失败'}));
            }
            response.end();
        })
    }

    注意:

    1、客户端发送ajax请求时

        如果data是字符串,接收过来就是 '{'username': 'aaa', 'password': 'ssssss'}' 字符串可以直接转成json,

        如果data传的是一个对象,jquery会转成"{'username'='aaa'&'password'='ssssss'}", 还需要node重组

http概念

1、up 工具 通过nodeAPI来监听文件或目录下所有文件,如果有文件改变不需要重启服务器

    $ sudo npm i up -g   

    运行命令: up -watch -port 8080 server.js 

nodeJS (之二) FileSystem

| open() - 打开文件
| close() - 关闭文件
| stat() - 文件信息
| read()、write() - 读文件数据并写入Buffer
| readFile()、readFileSync() - 读、写取文件,可以读图片并写出图片,但不能写流文件
| createReadStream()、createWriteStream() - 读/写文件流
| rename()、renameSync() - 修改文件或目录
| ftruncate() - 截断文件
| appendFile() - 将数据追加到文件中
| mkdir() - 创建/删除目录
| rmdir() - 删除空目录
| readDir()、readdirSync() - 读取目录下所有文件
|
| stat() - 文件信息
| utimes() - 修改文件时间
| watchFile() - 监视文件
| access() - 判断文件或目录权限
| link() - 硬链接
| unlink() - 删除文件链接
| symlink() - 符号链接
| readlink() - 读取链接源地址
| watchFile()、watch() - 侦听文件/文件夹内文件的变化

fs 模块引用

var fs = reuqire('fs');        // 加载文件模块

文件系统方法有异步和同步两种

1、readFile()与createReadStream()区别

    readFile是整个文件都读取完后在载入内存中,createReadStream()分片来读取内容块,直接读取完成,缓存与文件流的区别

open() 异步打开文件

open(文件路径, 打开方式(读/写),设置模式(读4/写2/执行1), callback(err, fd文件句柄)): 异步打开文件

    打开方式: r 读、 w 写

opensync(): 同步打开文件

Example:
    const fileName = path.resolve(__dirname, 'test.txt');
    fs.open(fileName, 'r', (err, fd) => {
        console.log(err);
        fs.close();
    })

close() 关闭一个文件

fs.close(fd, callback)

readFile()、readFileSync()

同步和异步读取文件

readFile('filename', [编码方式], callback)

缺点: readFile()是先将数据全部读入内存,文件大时候,读取效率低下,而createReadStream会把文件分割成小块,然后通过事件和来最后得到数据

注意: 编码方式不设置返回的是Buffer, 如果设置了utf-8返回的是字符串

Example:
    fs.readFile('./nodeJS学习笔记.txt', 'utf-8', function(err, data){
        if(err){
            console.log('文件读取错误')
        }
        else{
            var buf = new Buffer(data);        // 将读取到的数据转成Buffer
            console.log(buf.toString());    // 二进制转成字符串
        }                
    })

writeFile() 向文件写内容

可以使用readFile读图片并写出图片,但不能写流文件

writeFile(filename, data, [flag], callback(err)err不为真写入成功) 向文件写内容,文件不存在自动创建文件

writeFileSync同步

    filename: 路径加文件名, 如果文件不存在会创建一个文件**

    data: 写入的内容可以是字符串或二进制

    flag: 对文件怎么来操作,  'w' 清空后在写入  'a' 将新内容加到之后  {flag: 'w'}

    writeFileSync('文件名', '内容'): 同步写文件

Example: 
    var writeBuf = new Buffer(writeStr);

    fs.writeFile('./buffer/file/user.txt', writeStr, {flag: 'a'}, function(err){
        err ? console.log('文件写入失败') : console.log('文件写入成功');
    })

createReadStream(path, [options]): 读取文件流

fs模块允许你通过Stream来对数据进行读取操作,与readFile、writeFile不同是它对内存分配不是一次完成的.

options:
    { 
        flags: 'r',            // r只读 w写
        encoding: null,        // 字符编码 utf-8
        fd: null,
        mode: 0666,
        autoClose: true,    // 读完数据是否关闭文件描述符
        start: 0,            // 读取流的开始
        end: 300            // 读取流的结束
    }

Example: 
    var fs = require('fs');
    var rs = fs.createReadStream(path.resolve(basePath, 'test.txt'), {statr:100});
    var str = '';        // 存储读取的数据

    // 读取数据
    rs.on('data', (data)=>{
        str += data;
    })

    // 数据读取完成
    rs.on('end', ()=>{
        console.log('读取完成')
        console.log(str);            // 打印出读取数据
    })

    // 读取出错
    rs.on('error', (err) => {
        console.log(err);
    })

createWriteStream(文件路径, {options}): 可写数据流

write(): 写入内容, 把文件内容清空后在写入

end(): 关闭写入

    var fs = require('fs');
    var ws = fs.createWriteStream('./files/stream.txt');

    // 写入内容
    ws.write('内容加入到文件中', 'utf-8', function(){
        console.log(arguments);
    })
    ws.end(function(){

    })

rename() 修改文件或目录名

rename(oldPath, newPath, callback) 修改文件或目录名, renameSync同步

Example: 
    let oldName = path.resolve(basePath, 'newTest.txt');
    let newName = path.resolve(basePath, 'test.txt');
    fs.rename(oldName, newName, (err) => {
        if(err){
            console.log(err);
        }
        console.log('文件名修改成功')
    })

copyFile(f1, f2) 复制文件内容

copyFile('a.txt', 'b.txt');

ftruncate() 截断文件

ftruncate(文件路径, 第几个字符截断, callback) 截断文件, ftruncateSync同步

一个中文四个字节,英文两个字节

Example: 
    const basePath = __dirname;
    fs.open(path.resolve(basePath, 'test.txt'), 'r+', (err, fd)=>{
        if(err){
            console.log(err);
        }
        else{
            fs.truncate(fd, 400, (err)=>{        // 截取四百个字节
                var readNewFile = fs.readFile(fd, 'utf8', (err, data)=>{    // 读取文件
                    var buf = new Buffer(data);
                    console.log(buf.toString('utf-8'));        //
                    fs.close();
                });
            })
        }
    })

appendFile(): 将数据追加到文件中

appendFile(文件名, 加入内容): 异步地追加数据到一个文件,如果文件不存在则创建文件,与writeFile()中设置flag: 'a' 一样

Example: 
    fs.appendFile(path.resolve(basePath, 'append.txt'), '这是要加入的字符串', (err) => {
        console.log('complate');
    })

read() 指定的文件中读取数据,并写入缓存区buffer

read(fd, buffer, offset, length, position, callback): 读文件

// read(路径, 指定存到的缓存区, buf的开始, buf的结束, 文件读取的起启位置, callback)

Example: 
    var buf = new Buffer(6);
    fs.open(path.resolve(basePath, 'append.txt'), 'r', (err, fd) => {
        if(err){
            console.log(err);
            return false;
        }

        fs.read(fd, buf, 0, buf.length, 3, (err, bytesRead, buffer) => {
            var str = new Buffer(buffer);
            console.log(str.toString())
            console.log('bytesRead : ' + bytesRead);
        })
    })

write() 指定的文件中写数据

fs.write(fd, buffer, offset, length, position, callback)

二、操作目录

mkdir(目录名, callback(err))、rmdir(): 创建/删除目录

fs.mkdir('js/lib', function(err){        // 可以连续创建子目录, 先创建js,并在js目录下在创建lib目录
    console.log(err)
})

rmdir(path, callback): 删除空目录

readdir(path, callback(err, files)): 获取目录下所有文件

eaddirSync()同步

files: 将目录下所有文件返回一个数组

fs.readdir(basePath, 'utf-8', (err, files) => {
    if(err){
        console.log('文件目录获取失败', err);
    }
    else{
        files.forEach(function(data){
            var buf = new Buffer(data);
            console.log(buf);       // 输出文件名的二进制
        })
    }
})

三、文件权限

chown() 文件所有者变更

fs.chown(文件名,uid,gid,回调函数);
fs.fchown(文件句柄fd,uid,gid,回调函数);
fs.lchown(链接路径,uid,gid,回调函数);    

chmod()文件权限变更

fs.chmod(文件名,mode,回调函数);
fs.fchmod(文件句柄,mode,回调函数);
fs.lchmod(链接路径,mode,回调函数);

stat() 文件信息

也可以判断文件是否存在

fs.stat(文件路径, 回调函数(err.fs.Stats对象)); 获取文件或目录下的文件详情
fs.fstat(文件句柄fd, 回调函数(err.fs.Stats对象)); 显示文件或文件系统状态
fs.lstat(链接路径, 回调函数(err.fs.Stats对象)); 显示文件或文件系统状态(符号链接)

stat(path, callback(err, stats)): 获取文件详情, statSync同步

    fs.stat(`${dir}/img/readText.txt`, function (err, stats) {
        console.log(stats);                    // stats类
        console.log(stats.isFile());        // true
    })

    stats.isFile() - 是否是文件
    stats.isDirectory() - 是否是目录
    stats.isBlockDevice() - 是否是块设备
    stats.isCharacterDevice() - 是否是是字符
    stats.isSymbolicLink() (仅对 fs.lstat() 有效) - 是否是软链接
    stats.isFIFO() - 是不是FIFO
    stats.isSocket() - 是不是Socket


Example: fileSystem目录下有两个文件, a.txt、main.js

    fs.readdir('./fileSystem', function(err, files){
        files.forEach(function(file){
            console.log(file)
            fs.stat('./fileSystem/'+file, function(erro, stats){
                console.log('是否是目录:' + stats.isDirectory());
                console.log('是否是文件:' + stats.isFile());
                console.log(stats);      // 输出所有文件的详情
            })
        })
    })

utimes() 修改文件时间

fs.utimes(文件路径,访问时间,新建时间,回调函数);
fs.futimes(文件句柄,访问时间,新建时间,回调函数);      

watchFile()监视文件

fs.watchFile(文件名,[options],listener_callback(当前文件的stats,改变前的stats));      
fs.unwatchFile(文件名);

access(path, [mode], callback): 也是判断文件或目录的权限

fs.constants.F_OK - path 文件对调用进程可见。 这在确定文件是否存在时很有用,但不涉及 rwx 权限。 如果没指定 mode,则默认为该值。
fs.constants.R_OK - path 文件可被调用进程读取。
fs.constants.W_OK - path 文件可被调用进程写入。
fs.constants.X_OK - path 文件可被调用进程执行。 对 Windows 系统没作用(相当于 fs.constants.F_OK)

四、链接文件操作

创建一个硬链接

fs.link(srcpath, dstpath, [callback]): 硬链接 

fs.symlink(destination, path, [type], [callback]): 符号链接

读取链接指向的路径

fs.readlink(path, [callback(err,linkstr)]): 读取链接源地址 

fs.unlink(path,[callback]); 删除文件链接

watchFile()、watch(): 侦听文件/文件夹内的文件的变化

unwatchFile(): 停止侦听

// 侦听文件
fs.watch('./buffer/file/password.txt', {encoding: 'buffer'}, (eventType, filename) => {
    console.log(eventType, filename);
    if (filename){
        console.log(filename);
    }
});

// 向password.txt写内容
fs.writeFile('./buffer/file/password.txt', '添加新内容', {flag: 'a'}, function(err, data){
    if(!err){
        console.log('写入成功');
    }
})