Node.js之文件及文件流(fs,path,buffer,stream)

Posted 秋天1014童话

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Node.js之文件及文件流(fs,path,buffer,stream)相关的知识,希望对你有一定的参考价值。

文件操作

1 文件模块fs

fs模块---》操作文件---》io----》node的特长
fs模块是node非常重要的模块,能体现出node的优势
  • fs.readFile() 读文件
  • fs.writeFile() 写文件
  • fs.appendFile() 在文件的内部去追加写一些内容
  • fs.mkdir() 创建文件夹
  • fs.readdir() 读文件夹
  • fs.access() 判断路径(文件或者文件夹是都存在)
  • fs.stat()
    isFile:用于判断被查看的对象是否为一个文件,如果是返回true,否则,返回false;
    isDirectory:用于判断被查看的对象是否为一个目录,如果是的话则返回true,否则,返回false;
    isSymbolicLink:用于判断被查看的文件是否为一个符号链接文件,如果是的话则返回true,否则,返回false。该方法仅在lstat方法的回调函数中有效;

访问文件路径问题:

//目录结构:
lujing
    lib
        lujingTest.js
        foo.txt
    test.js
    foo.txt


//lujing\\foo.txt
外层的foo.text

//lujing\\lib\\foo.txt
内层的foo.text

-----------------
//示例一

//lujing\\test.js
var rf = require('./lib/lujingTest.js');

rf('./foo.txt',function(data)
    console.log(data);
)

//lujing\\lib\\lujingTest.js
var fs = require('fs');
var rf = function(path,callback)
    fs.readFile(path,'utf-8',(err,data)=>
        callback(data)
    )

module.exports = rf;

测试以上代码:node test.js,输出结果:外层的foo.text

总结:require引入模块,路径是相对于调用的模块的。可以理解为,把require进来的模块拿到调用的模块执行,仿require.js的代码可以看上篇博客
但要是想访问内层的foo.text,可以使用全局变量__dirname,代码如下:

//示例二

//lujing\\test.js
var rf = require('./lib/lujingTest2.js'); //!!!改为lujingTest2.js

rf('./foo.txt',function(data)
    console.log(data);
)

//lujing\\lib\\lujingTest2.js
//使用__dirname获取当前文件的文件夹路径
var fs = require('fs');
var rf = function(path,callback)
    fs.readFile(__dirname+'/foo.txt','utf-8',(err,data)=>
        callback(data)
    )

module.exports = rf;

问题: 上面代码在拼接路径时使用__dirname+'/foo.txt',有时写成__dirname+',/foo.txt',就会出现问题。可以使用path.join(__dirname,'./foo.text'),就会为我们解决这个问题.

var fs =require('fs');
var path=require('path');
var rf=function(cb)
    console.log(path.join(__dirname,'./foo.text'));
    fs.readFile(path.join(__dirname,'./foo.text'),'utf8',(err,data)=>
            cb(data);
    )

module.exports=rf;

path下面详解。

writeFile:

//第一参数路径
//第二个写的内容
//第三个编码格式
//第四个回调函数 

//多次写入会把原来的内容覆盖
//文件不存在的时候会创建文件,但是不会创建文件夹(找不到报错!)

var fs = require('fs');
fs.writeFile('./foo.txt', '我是写入的内容write', 'utf-8', (err)=>
    if (err) 
        console.log(err);
     else 
        fs.readFile('./foo.txt', 'utf-8', (err, data) => 
            console.log(data);
        )
    
)

//找不到a文件夹,报错
fs.writeFile('./a/foo.txt', '我是写入的内容write', 'utf-8', (err)=>
    if (err) 
        console.log(err);
     else 
        。。。
    
)

fs.appendFile('./foo.txt','我是追加的内容','utf-8',(err)=>
    if (err) 
        console.log(err);
     else 
        fs.readFile('./foo.txt', 'utf-8', (err, data) => 
            console.log(data);
        )
    
)

创建、读取文件夹(Linux命令)

fs.mkdir('./a',(err)=>

)

//callback 里面的参数多了一个files
//files以数组的形式呈现
fs.readdir('../file',(err,files)=>
    console.log(files);
)
//结果:[foo.txt,a,writeFile.js]

fs.readdir('../file',(err,files)=>
    console.log(files+"1");
)
//结果:foo.txt,a,writeFile.js1

access:

//access不光可以判断文件还可以判断文件夹
fs.access('./a',(err)=>
)

fs.stat

fs.stat(path,callback)
//获取文件信息对象Stats,包括文件大小,gid等信息

//文件信息对象Stats的一个方法,判断当前文件是不是一个文件

stats.isDirectory()
//文件信息对象Stats的一个方法,判断当前文件是不是一个文件夹

fs.stat('./a',(err,stats)=>
   if(stats.isDirectory())
      console.log("是文件夹");
   
   else
       console.log("不是文件夹");
      
)

2 path:

var path=require('path');

path.resolve('../../test');
//一个路径或路径片段解析成一个绝对路径,返回解析后的路径字符串

console.log(path.join(__dirname,'./foo.text'));
//假如__dirname为‘\\a’,则结果为:\\a\\foo.txt

console.log(path.basename('C:\\\\temp\\\\myfile.html')); 
//(文件名称+后缀名)myfile.html

console.log(path.basename('/foo/bar/baz/asdf/quux.html', '.html'));
//(文件名称)quux

console.log(path.dirname('/foo/bar/baz/asdf/quux.js'));
//结果(文件路径):/foo/bar/baz/asdf

console.log(path.extname('/foo/bar/baz/asdf/index.js.css.html'));
//结果(后缀名): .html

实例:深度遍历文件夹下所有文件:

const fs = require('fs');
const path = require('path');

//解析需要遍历的文件夹,获取绝对路径 
var filePath = path.resolve('../../test');
fileDisplay(filePath); //

function fileDisplay(filePath) 
    fs.readdir(filePath, function(err, files) 
        if (err) 
            console.log(err);
         else 
            files.forEach(function(filename) 
                //获取当前文件的绝对路径
                var filedir = path.join(filePath, filename);
                fs.stat(filedir, function(err, stats) 
                    if (err) 
                        console.warn('获取文件stats失败');
                     else 
                        if (stats.isFile()) 
                            console.log(filedir);
                        
                        if (stats.isDirectory()) 
                            //递归,如果是文件夹,就继续遍历该文件夹下面的文件
                            fileDisplay(filedir);
                        
                    
                )
            )
        
    )

运行结果:

G:\\html\\mysuccesstest\\node\\node-biji\\test\\新建文本文档.txt
G:\\html\\mysuccesstest\\node\\node-biji\\test\\.vscode\\launch.json
G:\\html\\mysuccesstest\\node\\node-biji\\test\\debug\\01.js
G:\\html\\mysuccesstest\\node\\node-biji\\test\\debug\\mytest.js
G:\\html\\mysuccesstest\\node\\node-biji\\test\\file\\foo.txt
G:\\html\\mysuccesstest\\node\\node-biji\\test\\file\\path.js
G:\\html\\mysuccesstest\\node\\node-biji\\test\\file\\writeFile.js
G:\\html\\mysuccesstest\\node\\node-biji\\test\\fileStream\\test.js
G:\\html\\mysuccesstest\\node\\node-biji\\test\\fileTraverse\\fileDisplay.js
G:\\html\\mysuccesstest\\node\\node-biji\\test\\fileTraverse\\fileTraverse.js
G:\\html\\mysuccesstest\\node\\node-biji\\test\\lujing\\foo.txt
G:\\html\\mysuccesstest\\node\\node-biji\\test\\lujing\\test.js
G:\\html\\mysuccesstest\\node\\node-biji\\test\\lujing\\test2.js
G:\\html\\mysuccesstest\\node\\node-biji\\test\\myFS\\index.js
G:\\html\\mysuccesstest\\node\\node-biji\\test\\myFS\\text.js
G:\\html\\mysuccesstest\\node\\node-biji\\test\\lujing\\lib\\foo.txt
G:\\html\\mysuccesstest\\node\\node-biji\\test\\lujing\\lib\\lujingTest.js
G:\\html\\mysuccesstest\\node\\node-biji\\test\\lujing\\lib\\lujingTest2.js
G:\\html\\mysuccesstest\\node\\node-biji\\test\\myFS\\node_modules\\readf\\index.js
G:\\html\\mysuccesstest\\node\\node-biji\\test\\myFS\\node_modules\\readf\\package.json

3 Buffer:

在Node中,用用需要处理网络协议、操作数据库、处理图片、接收上传文件等,在网络流和文件的操作中,还要大量处理二进制数据,js自有的字符串远远不能满足这些需求,于是Buffer对象应运而生。

Buffer类似于Array对象,但它主要操作字节。Buffer是一个典型的使用js和C++结合的模块,性能相关部分使用C++,费性能相关部分使用js。

Buffer所占用的内存不是通过V8分配的,属于堆外内存。由于V8垃圾回收性能的影响,将常用的操作对象用更高效和专有的内存分配回收策略来管理是个不错的思路。

1汉字=2字节  00100010 00000000
1字节(Byte)=8字位=8个二进制数 
1字位(bit)=1个二进制数 
1B=8b 
1KB=1024B 
1MB=1024KB 
1GB=1024MB 

3.1 创建Buffer

Buffer在Node启动时就加载了它,并将其放在全局对象上。所以不需要使用require()即可使用

Buffer类似于数组,元素为16进制的两位数,表示0-255之间的数值。

new Buffer(size)
new Buffer(str,[encoding])
var buf = new Buffer(100);
buf[1] = 0x6E;
buf[0] = 0x61;
console.log(buf);
console.log(buf.toString());

若元素赋值小于0,则逐次加256,直到为0-255的数值;若元素大于255,就逐次减256,直到为0-255的数值;如果是小数,则舍弃小数部分,保留整数部分。

3.2 Buffer的一些属性和方法

buf[index] 通过下标访问 buffer 的某个字节的数据
- buf.indexOf(value,[byteOffset],[encoding]) 查找某个字符在 buffer 内存中的字节下标
- buf.includes(value,[byteOffset],[encoding])
- buf.length
- buf.slice([start,[end]])
- buf.toString([encoding],[start], [end])
- buf.write(string,[offset],[length],[encoding])

3.3 造成乱码的原因

  • 什么是字符集编码
  • 为什么要有编码
    • 计算机只能识别二进制
    • 为了让计算机可以识别字符,人类做了一个字典 二进制 -> 字符 的映射关系
  • 为什么会产生乱码
    • 文件编码和读取该文件的编码不一致导致的
  • 如何解决乱码
    • 让文件和读取的字符编码集一致即可
  • 如何解决 Node 原生不支持的一些编码
    • 通过 第三方包:iconv-lite
    • 该第三方包可以解决 gbk 等编码不支持的问题

4 文件流

  • 流对象 stream
  • fs.readFile()和fs.writeFile()问题?
    • 对大文件的处理,例如下载
  • 通过文件流的形式传输大文件
  const rs = fs.createReadStream(path1);
  const ws = fs.createWriteStream(path1);
  rs.pipe(ws);
  • 如何去控制流。
    我们可以通过监听stream对象里面的事件,去控制读写流。
const fs = require('fs');

const rs = fs.createReadStream('./1203上午.wmv');
const ws = fs.createWriteStream('./2.wmc');
rs.on('data', function(chunk) 
    console.log(chunk.length);
    ws.write(chunk);
)
rs.on('end', function() 
    console.log('结束了');
    ws.end();
)
//执行结果:
65536
65536
...
65536
65536
55661
结束了

传输时显示进度:

const fs = require('fs');

const rs = fs.createReadStream('./1204上午.wmv');
const ws = fs.createWriteStream('./2.wmc');
// rs.pipe(ws);

//取文件对相应的stats对象
var stats = fs.statSync('1204上午.wmv');
//文件大小
var count = stats.size;

var sumSize = 0;
rs.on('data', function(chunk) 
    // 每得到一个chunk(buffer),取一次长度并且加到data的值里面去
    sumSize = sumSize + chunk.length;
    console.log('传输进度:' + parseInt(sumSize / count * 100) + "%");
    ws.write(chunk);
)
rs.on('end', function() 
    console.log('结束了');
    ws.end();
)

以上是关于Node.js之文件及文件流(fs,path,buffer,stream)的主要内容,如果未能解决你的问题,请参考以下文章

Node.js——fs模块(文件系统),创建删除目录(文件),读取写入文件流

node.js学习笔记之写文件

node.js学习笔记之写文件

node fs模块

Node.js学习之路05——fs文件系统之文件的写入和读取

使用Node.js管理多个文件流