nodejs---fs模块,文件读写操作详解,自定义一个文件写入方法

news2024/11/18 15:26:18

fs模块导入

Node.js 同时支持 CommonJS 和 ES 模块系统(自 Node.js v12 以来)

// 两种模块导入方式
import * as fs from 'fs';// Es6:这种方式需要在package.json中配置"type": "module"
const fs = require('fs');// commonJs:如果你只有一个单文件js那么你还是要使用这种方式导入

是否使用ES6的导入方式,取决于你是否有一个配置文件,或者就使用require导入

fs的读写文件方法

fs提供的文件操作方法包含3中模式:回调式,阻塞式,promise式

这里使用stat()方法举例,

检查文件信息 stat()

const fs = require('fs');// commonJs
const txt = '../public/file.txt'

// 回调式
fs.stat(txt, (err, stats) => {
  if(err){
    console.log(err.message);
  }
  console.log(stats.isFile());
})

// 阻塞式
try {
  const stats = fs.statSync(txt);
} catch (err) {
  console.error(err);
}
console.log(stats.isFile());

// promise式
async function example() {
  try {
    const stats = await fs.stat(txt);
  } catch (err) {
    console.log(err);
  }
  console.log(stats.isFile());
}
example();

使用后面两种方法时需要使用try,catch来捕获错误,

这里要注意 :如果不存在txt路径的文件,会产生错误,这种错误式 被捕获的err提示的,并非式致命错误(直接终止程序),它会提示你没有这个文件,而后面对这个文件的操作也会无法执行,

读取文件内容 readFile()

// 读取文件, 读取文件内容,并返回一个字符串
fs.readFile(txt, 'utf8', (err, data) => {
  if(err){
    console.log(err.message);
  }
  console.log(data);
}) 

参数:文件路径,读取格式(默认式二进制流格式),回调参数,err错误信息,data读取的数据

同样的,readFile也支持三种模式:回调式,阻塞式,promise式,这里仅展示回调式

写入文件writeFile()

// 写入文件, 写入文件内容,
fs.writeFile(txt, 'hello world', (err) => {
  if(err){
    console.log(err.message);
  }
  console.log('写入成功');
})

参数:文件路径,写入的内容,回调参数,err错误信息

同样的,writeFile也支持三种模式:回调式,阻塞式,promise式,这里仅展示回调式

追加内容appendFile()

fs.appendFile(txt, 'hello world', (err) => {
  if(err){
    console.log(err.message);
  }
  console.log('追加成功');
})

参数:文件路径,写入的内容,回调参数,err错误信息

同样的,appendFile也支持三种模式:回调式,阻塞式,promise式,这里仅展示回调式

注意 writeFile会覆盖原内容,从起始位置开始写入,appendFile不覆盖原内容,从末尾位置开始写入

文件夹操作

判断一个文件夹是否存在
使用 fs.existsSync(),这里exists不推荐使用了,事实上,对于文件夹的操作都建议使用阻塞式,因为文件都必须在文件夹下操作,文件夹没有被获取的话,文件操作就无法正常执行
新建一个文件夹
使用 fs.mkdir() 或 fs.mkdirSync()  创建新文件夹
读取目录的内容
使用 fs.readdir() 或 fs.readdirSync() 读取目录的内容。
重命名文件夹
使用 fs.rename() 或 fs.renameSync()  重命名文件夹。
删除文件夹
使用 fs.rmdir() 或 fs.rmdirSync() 删除文件夹。

新建一个文件夹

这里展示如何新建一个文件夹,其他方法类似,想了解更多可以参考:在 Node.js 中使用文件夹 (nodejs.cn)

const fs = require('fs');
const path = "./public/";

try {
  // 如果没有public文件夹则创建
  if (!fs.existsSync(path)) {
    fs.mkdirSync(path);
  }
} catch (err) {
  console.error(err);
}

封装一个setFile方法

了解了基本的读写操作后我们可以封装一个方法,

该函数接收一个文件名和一个文件内容作为参数,并将文件内容写入指定的文件中(没有则创建),路径是当前目录下新建一个public文件夹,并将文件至于public下面。

/**
 * @param {文件名称} baseName 
 * @param {写入的内容} content 
 * @param {内容是否覆盖} b 默认为false,不覆盖
 */

 setFile(baseName,content,b)
module.exports.setFile = function (baseName, content, b = false) {
  // 如果没有public文件夹则创建
  try {
    if (!fs.existsSync(path)) {
      fs.mkdirSync(path);
      console.log("文件夹 public 创建成功");
    }
  } catch (err) {
    console.log(err);
  }

  // 判断文件是否存在,在open之前判断,回调结果在open之后
  fs.stat(path + baseName, (err) => {
    if (err) {// 文件不存在,产生错误
      // console.log(err);
      console.log(`文件 ${baseName} 创建成功`);
    }
  })

  if (!b) {
    // 不覆盖内容

    // fs.readFile(path + baseName, "utf-8",(err, data) => {
    //   if (err) {
    //     console.error(err);
    //   }
    //   // 读取出原来的内容拼接
    //   content = data + content;

    //   fs.writeFile(path + baseName, content, (err) => {
    //     if (err) {
    //       console.error(err);
    //     }
    //     console.log("写入成功");
    //   })
    // })

    fs.appendFile(path + baseName, content, (err) => {
      if (err) {
        console.error(err);
      } else {
        console.log("写入成功");
      }
    })

  } else {
    fs.writeFile(path + baseName, content, (err) => {
      if (err) {
        console.error(err);
      } else {
        console.log("写入成功");
      }
    })
  }

}

在同目录下新建一个useSetFile.js导入这个方法

// 导出的模块是整个文件,需要通过属性引入使用里面的方法
const setFile = require('./setFile');

const file = 'file.txt';
const content = 'Hello, World!';

setFile.setFile(file, content);

在控制台执行node useSetFile.js

 

可以看到目录下多了一个public文件夹和file.txt文件,并且写入了两个hello,world!

数据流缓冲区读写---open,close,read,write方法

出来file后缀的文件操作方法,fs模块还有基于数据流的读写方法,关于二进制数据流可以参考:

js二进制数据,文件---ArrayBuffer,二进制数组_js arraybuffer()-CSDN博客

// 打开文件,'w+/a+' 获得读,写权限,文件流起始位置在开头/末尾(a相比于a+没有读取权限), 如果文件不存在则创建,读取文件后会覆盖原有文件
fs.open(txt, 'a', (err, fd) => {
  if(err){
    console.log(err.message);
  }
  // 关闭文件, 关闭文件后才能进行其他操作
  fs.close(fd, (err) => {
    if(err){
      console.log(err.message);
    }
    console.log('文件关闭成功');
  })
})



/**
 * fd, 使用fs.open打开成功后返回的文件描述符
 * buffer, 一个Buffer对象,v8引擎分配的一段内存
 * offset, 整数,从缓存区中读取/写入时的初始位置,以字节为单位
 * length, 整数,从缓存区中读取/写入数据的字节数
 * position, 整数,写入/读取文件初始位置;
 * callback(err, written, buffer), 写入操作执行完成后回调函数,written实际写入字节数,buffer被读取的缓存区对象
 */
fs.write(fd, buffer, offset, length, position, callback);
fs.read(fd, buffer, offset, length, position, callback);

整体的使用结构为

open->read/write->close

open开始拿到fd,fd为一个整数,表示打开文件返回的文件描述符,window中又称文件句柄

后续操作以fd为文件标志进行操作

const fs = require('fs');
const path = __dirname+"/public/buffer.txt";// __dirname 当前文件所在目录的绝对路径
const buffer = new Buffer.from("你好");  //每一个汉字utf8编码是3个字节,英文是1个字节

fs.open(path, 'a+', (err, fd) => {
  fs.write(fd,buffer,0,6,0, (err) => {// 0,6,0的含义,0,6从buffer中读取6个字节,0是开始读取的位置,0是写入的位置,写入文件的起始位置  
    if(err){
      console.log(err);
    }else{
      console.log("写入成功");
    }
  })

  fs.read(fd,buffer,0,6,0, (err, bytes) => {// 0,6,0的含义,0,6从buffer中写入6个字节,0是开始写入的位置,0是读取的位置,读取文件的起始位置
    if(err){
      console.log(err);
    }else{
      if(bytes > 0){
        console.log(buffer.slice(0, bytes).toString());//打印buffer中的内容

        fs.read(fd,buffer,0,6,null, (err, bytes) => {// 再次读取fd中的内容时,不标记出读取位置,默认为上次读取结束的位置,'a+'表示每次将位置放置在文件末尾
          if(err){
            console.log(err);
          }else{
            if(bytes > 0){
              console.log(buffer.slice(0, bytes).toString());
            }
          }
        })
      }

      fs.close(fd, (err) => {//结束本次文件读写
        if(err){
          console.log(err);
        }else{
          console.log("关闭文件成功");
        }
      })
    }
  })

});

这种方式的逻辑更加精细,可以控制数据流缓存区的位置进行读写,

值得注意的是,read和write两个方法中的3个整数参数,前两个是指定给buffer数据流缓冲区的 ,最后一个是文件的位置,当你进行read读取时,是对文件进行读取,在将读取的内容写入buffer

当你进行write写入时,是对buffer进行读取,在将读取的内容写入文件 ,两者正好相反但是参数格式一致

完整代码总结和目录结构展示

file.js

// 两种模块导入方式
// import * as fs from 'fs';// Es6:这种方式需要在package.json中配置"type": "module"
const fs = require('fs');// commonJs
const path = require('path');

const txt = '../public/file.txt'
const txtMsg = [
  path.dirname(txt),// 父级文件夹
  path.basename(txt),// 文件名,包括后缀
  path.extname(txt) // 文件后缀(扩展名)
]

let f = 0 ;

txtMsg.forEach(item => {
  console.log(item);
})

// 同一代码块的回调执行顺序是 fs.open -> fs.close ->fs.stat -> fs.writeFile -> fs.readFile 打开文件,关闭文件,查看文件,写入文件,读取文件
// 打开文件,'w+/a+' 获得读,写权限,文件流起始位置在开头/末尾(a相比于a+没有读取权限), 如果文件不存在则创建,读取文件后会覆盖原有文件
fs.open(txt, 'a', (err, fd) => {
  if(err){
    console.log(err.message);
  }
  f = fd;
  console.log(fd); 
})

// 读取文件, 读取文件内容,并返回一个字符串
fs.readFile(txt, 'utf8', (err, data) => {
  if(err){
    console.log(err.message);
  }
  console.log(data);
}) 

// 写入文件, 写入文件内容,
fs.writeFile(txt, 'hello world', (err) => {
  if(err){
    console.log(err.message);
  }
  console.log('写入成功');
})

// 追加文件内容
fs.appendFile(txt, 'hello world', (err) => {
  if(err){
    console.log(err.message);
  }
  console.log('追加成功');
})

// 关闭文件, 关闭文件后才能进行其他操作
fs.close(f, (err) => {
  if(err){
    console.log(err.message);
  }
  console.log('文件关闭成功');
})

// 文件信息
fs.stat(txt, (err, stats) => {
  if(err){
    console.log(err.message);
  }
  console.log(stats.isFile());
})

// 每次执行此文件,会先打开txt.txt文件,(此时内容已清空)关闭文件,写入内容,再读取文件内容

setFile.js

// 封装一个setFile函数,该函数接收一个文件名和一个文件内容作为参数,并将文件内容写入指定的文件中(没有则创建),路径是当前目录下新建一个public文件夹,并将文件至于public下面。
// setFile(baseName,content,b)

const fs = require('fs');
const path = "./public/";

/**
 * @param {文件名称} baseName 
 * @param {写入的内容} content 
 * @param {内容是否覆盖} b 默认为false,不覆盖
 */

module.exports.setFile = function (baseName, content, b = false) {
  // 如果没有public文件夹则创建
  try {
    if (!fs.existsSync(path)) {
      fs.mkdirSync(path);
      console.log("文件夹 public 创建成功");
    }
  } catch (err) {
    console.log(err);
  }

  // 判断文件是否存在,在open之前判断,回调结果在open之后
  fs.stat(path + baseName, (err) => {
    if (err) {// 文件不存在,产生错误
      // console.log(err);
      console.log(`文件 ${baseName} 创建成功`);
    }
  })

  if (!b) {
    // 不覆盖内容

    // fs.readFile(path + baseName, "utf-8",(err, data) => {
    //   if (err) {
    //     console.error(err);
    //   }
    //   // 读取出原来的内容拼接
    //   content = data + content;

    //   fs.writeFile(path + baseName, content, (err) => {
    //     if (err) {
    //       console.error(err);
    //     }
    //     console.log("写入成功");
    //   })
    // })

    fs.appendFile(path + baseName, content, (err) => {
      if (err) {
        console.error(err);
      } else {
        console.log("写入成功");
      }
    })

  } else {
    fs.writeFile(path + baseName, content, (err) => {
      if (err) {
        console.error(err);
      } else {
        console.log("写入成功");
      }
    })
  }
}

useSetFile.js

// 导出的模块是整个文件,需要通过属性引入使用里面的方法
const setFile = require('./setFile');

const file = 'file.txt';
const content = 'Hello, World!';

setFile.setFile(file, content);

buffer.js

const fs = require('fs');
const path = __dirname+"/public/buffer.txt";// __dirname 当前文件所在目录的绝对路径
const buffer = new Buffer.from("你好");  //每一个汉字utf8编码是3个字节,英文是1个字节

fs.open(path, 'a+', (err, fd) => {
  fs.write(fd,buffer,0,6,0, (err) => {// 0,6,0的含义,0,6从buffer中读取6个字节,0是开始读取的位置,0是写入的位置,写入文件的起始位置  
    if(err){
      console.log(err);
    }else{
      console.log("写入成功");
    }
  })

  fs.read(fd,buffer,0,6,0, (err, bytes) => {// 0,6,0的含义,0,6从buffer中写入6个字节,0是开始写入的位置,0是读取的位置,读取文件的起始位置
    if(err){
      console.log(err);
    }else{
      if(bytes > 0){
        console.log(buffer.slice(0, bytes).toString());//打印buffer中的内容

        fs.read(fd,buffer,0,6,null, (err, bytes) => {// 再次读取fd中的内容时,不标记出读取位置,默认为上次读取结束的位置,'a+'表示每次将位置放置在文件末尾
          if(err){
            console.log(err);
          }else{
            if(bytes > 0){
              console.log(buffer.slice(0, bytes).toString());
            }
          }
        })
      }

      fs.close(fd, (err) => {//结束本次文件读写
        if(err){
          console.log(err);
        }else{
          console.log("关闭文件成功");
        }
      })
    }
  })

});

目录结构

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1799469.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【操作系统】进程与线程的区别及总结(非常非常重要,面试必考题,其它文章可以不看,但这篇文章最后的总结你必须要看,满满的全是干货......)

目录 一、 进程1.1 PID(进程标识符)1.2 内存指针1.3 文件描述符表1.4 状态1.5 优先级1.6 记账信息1.7 上下文 二、线程三、总结:进程和线程之间的区别(非常非常非常重要,面试必考题) 一、 进程 简单来介绍一下什么是进程&#xf…

【人工智能】流行且重要的智能算法整理

✍🏻记录学习过程中的输出,坚持每天学习一点点~ ❤️希望能给大家提供帮助~欢迎点赞👍🏻收藏⭐评论✍🏻指点🙏 小记: 今天在看之前写的文档时,发现有人工智能十大算法的内容&#xf…

stdlib.h: No such file or directory

Qt报错: error: stdlib.h: No such file or directory #include_next <stdl 报错, 其他博主的解决方法: Qt报错: error: stdlib.h: No such file or directory #include_next <stdl_qt5.15 无法打开包括文件“stdlib.h” no suc…

数据结构之ArrayList与顺序表(上)

找往期文章包括但不限于本期文章中不懂的知识点: 个人主页:我要学编程(ಥ_ಥ)-CSDN博客 所属专栏:数据结构(Java版) 顺序表的学习,点我 上面这篇博文是关于顺序表的基础知识,以及顺序表的实现。…

第八篇——矢量化:象形文字和拼音文字是如何演化的?

目录 一、背景介绍二、思路&方案三、过程1.思维导图2.文章中经典的句子理解3.学习之后对于投资市场的理解4.通过这篇文章结合我知道的东西我能想到什么? 四、总结五、升华 一、背景介绍 通过这篇看似在讲文字的演化过程,实际是在说人生应该如何走&a…

中文文案写作有哪些合适的AIGC工具?

这是计育韬老师第 8 次开展面向全国高校的新媒体技术公益巡讲活动了。而在每场讲座尾声,互动答疑环节往往反映了高校师生当前最普遍的运营困境,特此计老师在现场即兴答疑之外,会尽量选择有较高价值的提问进行文字答疑梳理。 *本轮巡讲主题除了…

Solon2分布式事件总线的应用价值探讨

随着现代软件系统的复杂性日益增加,微服务架构逐渐成为开发大型应用的主流选择。在这种架构下,服务之间的通信和协同变得至关重要。Solon2作为一个高性能的Java微服务框架,其分布式事件总线(Distributed Event Bus)为微…

重回1990短视频全集:成都鼎茂宏升文化传媒公司

重回1990短视频全集:时光之旅的温情回顾 在数字技术的浪潮中,短视频以其独特的魅力迅速崛起,成为我们记录生活、分享故事的新方式。而当我们回望过去,那些充满怀旧情怀的年份总是让人心生感慨。今天,就让我们一起踏上…

Day45 代码随想录打卡|二叉树篇---路径总和

题目(leecode T112): 给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;…

VMware虚拟机与MobaXterm建立远程连接失败

VMware虚拟机与MobaXterm建立远程连接失败 首先可以检查一下是不是虚拟机的ssh服务并不存在 解决方法: 1.更新镜像源 yum -y update 这个过程会有点久,请耐心等待 2.安装ssh yum install openssh-server 3.启动ssh systemctl restart sshd 4.查…

【Spring Cloud Alibaba】13.自建存储对象服务与集成(minio版)

文章目录 简介什么是云存储服务(OSS)为什么选择MiniIOMiniIO相关地址 搭建(docker)安装Docker部署MinIO创建存储桶配置存储桶设置存储桶可以直接在浏览器访问 集成到Spring Cloud Alibaba项目创建子模块引入依赖包项目结构配置文件工具类接口类测试 简介…

iBeacon赋能AR导航:室内定位技术的原理与优势

室内定位导航对于大型商场、机场、医院等复杂室内环境至关重要,它帮助人们快速找到目的地,提高空间利用率。AR技术通过将虚拟信息叠加在现实世界,提供直观导航指引,正在成为室内导航的新趋势,增强用户互动体验&#xf…

java基础-深拷贝和浅拷贝

java中有一个概念叫深拷贝和浅拷贝,那这两个是什么意思呢?其实你可以对比一下c中的传值和传引用的问题。 深拷贝 即两个相同的对象地址不同,比如对象A通过拷贝出来对象B,在对B对象进行操作时不会影响到A对象的内容。 浅拷贝 和…

Facebook革新:数字社交的下一个阶段

在数字化时代,社交网络已经成为人们生活中不可或缺的一部分。作为全球最大的社交网络平台之一,Facebook一直在不断创新,引领着数字社交的发展。然而,随着科技的不断进步和社交需求的变化,Facebook正在走向一个新的阶段…

C#——break、continue、goto关键字的使用

break break是搭配循环语句使用的,用于跳出循环。 举例 : 当for循环执行到第5次时,使用break方法 跳出循环。 continue continue 语句的工作原理与 break 语句类似,但是 continue 语句并不会跳出整个循环,而是跳过本次循环继续执…

【TB作品】MSP430F5529 单片机,温度控制系统,DS18B20,使用MSP430实现的智能温度控制系统

作品功能 这个智能温度控制系统基于MSP430单片机设计,能够实时监测环境温度并根据预设的温度报警值自动调节风扇和加热片的工作状态。主要功能包括: 实时显示当前温度。通过OLED屏幕显示温度报警值。通过按键设置温度报警值。实际温度超过报警值时&…

Docker高级篇之Docker搭建mysql主从复制架构

文章目录 1. 安装mysql主从复制2. 主从复制测试 1. 安装mysql主从复制 首先创建主节点 docker run -d -p 3308:3306 \ --privilegedtrue \ -v /Users/jackchai/Desktop/lottory_docker/learndocker/mymysql/master/log:/var/log/mysql \ -v /Users/jackchai/Desktop/lottory_…

如何执行VMware P2V迁移|VMware Converter和替代方案

VMware中的P2V是什么? 我们常说的VMware P2V其实指的就是“物理到虚拟”,将工作负载从物理机器转换或迁移到虚拟机(VM)的过程,能够使您无需从头开始费力地创建和配置新虚拟机。 就像您可以使用Disk2vhd执行Hyper-V物理…

美团面试:百亿级分片,如何设计基因算法?

尼恩说在前面 在40岁老架构师 尼恩的读者交流群(50)中,最近有小伙伴拿到了一线互联网企业如得物、阿里、滴滴、极兔、有赞、希音、百度、网易、美团的面试资格,遇到很多很重要的架构类/设计类的场景题: 1.说说分库分表的基因算法&#xff1f…

Nginx 配置防护 缓慢的 HTTP拒绝服务攻击+点击劫持:X-Frame-Options未配置

一 安全团队检测网站 1 检测到目标主机可能存在缓慢的HTTP拒绝服务攻击 缓慢的HTTP拒绝服务攻击是一种专门针对于Web的应用层拒绝服务攻击,攻击者操纵网络,对目标Web服务器进行海量HTTP请求攻击,直到服务器带宽被打满,造成了拒绝服务。 慢…