通常呢,前端人员对于文件或者文件夹的操作相对而言是比较少的,现在呢有以下情景:假设某位大哥选择了一个excel文件并且传输到了后端,后端需要将这个文件解析并且存到指定的位置,然后解析成符合前端需要的数据写入到数据库中方便前端大哥调接口进行增删改查。我们现在呢cosplay的是一个后端同学,那么这些事自然而然就需要我们处理啦,这次记录的是内置模块fs,fs模块主要是用于文件和文件夹的操作,它内置了很多关于文件操作的方法来帮我们完成以上场景中的文件夹的存放解析以及读取。
文件夹基本操作:创建,重命名,删除
const fs = require('fs')
//在当前目录下创建一个avtar的目录
//fs.mkdir方法:在指定目录下创建一个新的目录
fs.mkdir("./avtar", (err) => {
//err是指错误参数
if (err && err.code === 'EEXIST') {
console.log("目录已存在")
}
})
//fs.rename方法:重命名当前文件夹
fs.rename("./avtar", "./person", (err) => {
console.log(err)//err只指错误参数
// [Error: ENOENT: no such file or directory, rename 'D:\CJ\nodeTest\node\fs文件操作模块\avtar' -> 'D:\CJ\nodeTest\node\fs文件操作模块\person'] {
// errno: -4058,
// code: 'ENOENT',
// syscall: 'rename',
// path: 'D:\\CJ\\nodeTest\\node\\fs文件操作模块\\avtar',
// dest: 'D:\\CJ\\nodeTest\\node\\fs文件操作模块\\person'
// }
if (err.code === 'ENOENT') {
console.log('当前文件不存在')
}
})
//删除当前文件夹
// 删除不为空的文件时,会报错 解决方法:unlink所有的文件,然后在执行rmdir
fs.rmdir("./person", (err) => {
console.log(err)//err只指错误参数
if (err && err.code === 'ENOENT') {
console.log('当前文件不存在')
// D:\CJ\nodeTest\node\fs文件操作模块>node 删除.js
// [Error: ENOENT: no such file or directory, rmdir 'D:\CJ\nodeTest\node\fs文件操作模块\person'] {
// errno: -4058,
// code: 'ENOENT',
// syscall: 'rmdir',
// path: 'D:\\CJ\\nodeTest\\node\\fs文件操作模块\\person'
}
}
})
对于上文提及的删除不为空的文件,这个作为一个重点,在后面写demo来实现。
经过以上操作,我们离一个合格的后端人员又进了一步,既然已经学会了文件夹的操作,那如何向文件夹中增加文件呢?
文件的创建、删除、编辑、读取
/*
* @Author: your name
* @Date: 2023-02-06 13:57:47
* @LastEditTime: 2023-02-06 14:23:09
* @LastEditors: Please set LastEditors
* @Description: In User Settings Edit
* @FilePath: \nodeTest\node\fs文件操作模块\添加文件.js
*/
const fs = require("fs")
// 如果没有文件,则添加文件,如果有文件,则覆盖之前的
fs.writeFile('./avtar/test1.txt', "清和节后绿枝愁,寂寞黄梅雨乍收\n", (err) => {
console.log(err)
})
// 向之前的文件中继续添加内容
fs.appendFile('./avtar/test1.txt', '畏日正常凝碧汉,熏风微度到丹楼', (err) => {
console.log(err)
})
// 读取之前的文件,回调函数的errorFirst风格,打印data
fs.readFile('./avtar/test1.txt', 'utf-8', (err, data) => {
console.log(err)
console.log(data) //添加UTF-8以后打印
// console.log(data) //包含Buffer对象
// console.log(data.toString('utf-8')) //按照UTF-8解码
})
// 要删除的文件
fs.unlink('./avtar/a.txt', (err) => {
console.log(err)
})
删除不为空的文件## 删除不为空的文件夹
学会了以上的基础操作,我们来解决一下前文提出的删除不为空的文件夹:
情景:一个文件夹,里面包含很多的子文件和子文件夹,要删除最外面的文件夹,得把所有的内容删除才能删除,那怎样解决这个问题呢?聪明的同学应该能想到:递归!没错,就是递归去执行删除操作,等到删除操作执行完在进行删除文件夹操作。
这个时候,聪明的同学就又会说啦:“不仅是递归,还有异步呢!”不得不说,这个同学是真的很聪明,确实是异步的。假设你说要删除的文件夹下包含的内容很大很多,递归肯定是循环遍历的,但是循环走完了可是删除的操作正在进行中呀,按照代码的逻辑循环走完就回去执行删除最外层目录的操作,这样的话肯定就会报错啦~~那么为了解决这个问题,我们分别看一下同步和异步的写法:
同步写法
/*
* @Author: your name
* @Date: 2023-02-06 15:11:04
* @LastEditTime: 2023-02-06 15:18:34
* @LastEditors: Please set LastEditors
* @Description: In User Settings Edit
* @FilePath: \nodeTest\node\fs文件操作模块\异步创建.js
*/
const fs = require('fs')
function delDictionary(filePath) {
fs.readdir(filePath, (err, data) => {
if (!err && data.length) {
// 异步调用,一定要等到删除完所有的子文件才能删除目录
// 当forEach执行完会立即执行删除操作,但是当文件较大时则会导致文件还未删除完毕已经去删除外层目录了
data.forEach(item => {
fs.unlinkSync(`./testDelete/${
item}`, () => {
}) //采用Sync同步写法,保证for循环之后立马删除文件
})
fs.rmdir('./testDelete', () => {
console.log()
})
}
})
}
delDictionary('./testDelete')
对于同步写法,最重要的一点就是它会阻塞后面代码的执行,假设在同一时间,我们在执行一个很大的文件操作,而碰巧业务这块刚好要调用一个刷新的接口,在此时只能等文件删除完了才能去掉接口,这个时候就会出现页面的卡顿和刷新的延迟,对于用户体验是很不友好的,因此我们决定采用异步的写法;
这个时候聪明又眼尖的同学就会发现以上文件夹和文件的操作都用到了回调函数,而解决异步编程的方法主要有以下几种:
-
callback
-
自定义事件
-
promise
-
async/await
由于callback存在回调地狱的问题,我们采用promise来简单实现以下上述操作:
由于callback存在回调地狱的问题,我们采用promise来简单实现以下上述操作:
```js
// 异步写法
const fs = require('fs').promises
fs.readdir('./异步创建').then(data => {
let arr = []
data.forEach(item => {
arr.push(fs.unlink(`./异步创建/${
item}`)
})
Promise.all(arr).then(res => {
fs.rmdir('./异步创建')
})
}).catch(() => {
})
以上代码还不够精简,我们再优化一下:
// 精简代码
fs.readdir('./异步创建').then(data => {
Promise.all(data.map(item =>fs.unlink(`./异步创建/${
item}`)).then(()=>{
fs.rmdir('./异步创建')
})
}).catch(() => {
})
或者采用以下写法:
fs.readdir('./异步创建').then(async (data)=> {
let arr = []
data.forEach(item => {
arr.push(fs.unlink(`./异步创建/${
item}`))
})
await Promise.all(arr)
await fs.rmdir('./异步创建')
}).catch(() => {
})
运行以下,就会发现~哇!成功啦!
fs.readdir('./异步创建').then(async (data)=> {
let arr = []
data.forEach(item => {
arr.push(fs.unlink(`./异步创建/${
item}`))
})
await Promise.all(arr)
await fs.rmdir('./异步创建')
}).catch(() => {
})
先记录到这里la~