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(之八)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(之十)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基 (之一) 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(之九)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 (之二) 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('写入成功');
    }
})

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);

Less

一、变量

@nice: #5b83ad
@light-blue: @nice + #111;

#header {
    background-color: @nice;
}

二、混合

定义一个类的样式

示例1:
    .bordered {
        border-top: 1px black solid;
        border-bottom: 1px black solid;
    }

    加载到#menu a内
    #menu a {
      color: #111;
      .bordered;
    }

    .post a {
      color: red;
      .bordered;
    }


示例2:混合带参数
    .border-radius(@values) {
          -webkit-border-radius: @values;
        -moz-border-radius: @values;
        border-radius: @values;
    }

    .box-radius{ width: @filler; heigth: 30px; border: 1px #ccc solid; .border-radius(4px) }


示例3:
    .a, #b {
        color: red;
    }
    .mixin-class {
        .a();
    }

    输出:
    .a, #b {
        color: red;
    }
    .mixin-class {
        color: red;
    }


示例4:
    .my-mixin {
        color: black;
    }
    .my-other-mixin() {
        background: white;
    }
    .class {
        .my-mixin;
        .my-other-mixin;
    }

    输出:
    .my-mixin {
        color: black;
    }
    .class {
        color: black;
        background: white;
    }

三、嵌套

示例1:

    #header{
        color: black;

        .navigation {
            font-size: 12px;
        }
    }

    output:

        #header {
            color: black;
        }
        #header .navigation {
            font-size: 12px;
    }

示例2:a 的 hover

    .box {
        a {
            color: red;
            $:hover {
                color: green;
            }
        }
    }

    output:
    .box a { color: red; }
    .box a:hover { color: green; }


示例3:arguments 可以将所有参数显示到值中

    .border-arg(@width, @color, @style){
        border: @arguments;            // 这里使用@arguments就相当于 @width @color @style
    }

四、运算

@base: 5%;
@filler: @base * 2;
.boxA { width: @filler; height: 20px; background-color: #c3c3c3; }

五、匹配模式

// 设置匹配
.trangle(top, @width, @color){
    border-width: @width; 
    border-color: transparent transparent @color transparent;
    border-style: dashed dashed solid dashed
}

.trangle(left, @width, @color){
    border-width: @width; 
    border-color: transparent @color transparent transparent;
    border-style: dashed dashed solid dashed
}

.trangle(right, @width, @color){
    border-width: @width; 
    border-color: transparent transparent transparent @color;
    border-style: dashed dashed solid dashed
}

.trangle(bottom, @width, @color){
    border-width: @width; 
    border-color: @color transparent transparent transparent;
    border-style: dashed dashed solid dashed
}

/* 匹配模式 */
.trangleL {
    width: 0; height: 0; overflow: hidden;
    .trangle(right, 18px, red)        /* 这里调用的right 就是右三角 */
}
<div class="trangleL"></div>

六、作用域 Scope

@var: red;
#page {
    @var: white;
    #header {
        color: @var; // white
    }
}

编译后:
#page .header {
    color: white;
}

七、注释

/* */    会被编译,输出会显示注释
//         不会被编译,输出不会显示注释

八、importe

导入less文件: @import 'libary';        不用加.less
导入css文件:     @import 'typo.css';    

九、url

@images: "../img";

// 用法
body {
    color: #444;
    background: url("@{images}/white-sand.png");
}

十、循环

.loop(@counter) when (@counter > 0) {        // 循环使用 定义函数名(参数) when (条件)
    .loop((@counter - 1));        // 每一次减1
    width: (10px * @counter);     // code for each iteration
}
div {
    .loop(3); // launch the loop
}

output:
div {
    width: 10px;
    width: 20px;
    width: 30px;
}

十一、合并

.mixin() {
    box-shadow+: inset 0 0 10px #555;
}
.myclass {
    .mixin();
    box-shadow+: 0 0 20px black;
}


.myclass {
    box-shadow: inset 0 0 10px #555, 0 0 20px black;
}

十二、避免编译使用 “~”

.text {
    width: ~(30px + 30px);        // 这里就不会被计算,而直接输出width: 30px + 30px;
}

十三、与构建工具结合

可以使用gulp 或 webpack 来进行编译

参考资料

http://less.bootcss.com
http://www.wtoutiao.com/p/15bzfg3.html
http://www.w3cplus.com/css/sass-vs-less-vs-stylus-a-preprocessor-shootout.html
http://www.w3cplus.com/preprocessor/architecture-sass-project.html