文件系统模块Node中最常见的内置模块Filesystem(fs),该模块提供了处理文件和目录的函数readFile:读取文件,并将文件内容传递给函数writeFile:将文件写入磁盘readdir:以字符串数组的形式返回目录中的文件stat:用于获取文件信息rename:用于重命名文件unlink:用于删除文件//异步版本varfs=requier('fs')fs.readFile('file.text','utf8',function(error,text){if(error){throwerror}console.log('Thefilecontained:',text)})//同步版本fs.readFileSync('file.text','utf8')异步回调函数通常遵循异步优先级,即第一个参数接收一个可能出错的对象,其余参数使用同步函数接收结果。节省代码,非常适合简单的脚本,但是异步函数会带来额外的速度提升和减少延迟链接分类硬链接(HardLink)硬链接是指通过索引节点链接。在Linux文件系统中,所有文件都会分配一个索引节点号inode,它是文件在系统中的唯一标识。文件的实际数据放在数据区(datablock),INode存储文件参数信息(metadata元数据),如创建时间、修改时间、文件大小、所有者、所属用户组、读写权限等,数据所在的块号等。多个文件名可以指向同一个索引节点(Inode)。硬链接只能在同一文件系统(磁盘)内的文件之间进行链接,不能创建目录。只要文件的索引节点有多个链接,其中一个链接改变文件的内容,其他链接的内容读取文件。只删除其中一个链接不影响索引节点本身和其他链接(数据的实体没有改变。删除),只有在删除最后一个链接时,如果此时有新的数据要存入磁盘时间,被删除文件的数据块和目录的链接将被释放,空间将被新数据暂时覆盖。软链接(SymbolicLink)软链接(也叫符号链接)类似于windows系统中的快捷方式。与硬链接不同,软链接是一个普通的文件,只是数据块的内容有点特殊,用户数据块中存放的文件内容是另一个文件的路径名的指向。这样可以快速定位到软链接指向的源文件实体。如果源文件被删除,软链接也会失效。可以跨文件系统为文件或目录创建软链接。扩展执行readFile函数返回Promisevarfs=require('fs')functionreadFilePromise(...args){returnnewPromise((resolve,reject)=>{fs.readFile(...args,(err,data)=>{if(err){reject(err)}else{resolve(data)}})})}readFilePromise('a.js').then(data=>{}).catch(err=>{})执行writeFile函数returnPromisevarfs=require('fs')functionwriteFilePromise(...args){returnnewPromise((resolve,reject)=>{fs.writeFile(...args,(err)=>{if(err){resolve(err)}else{reject()}})})}writeFilePromise('a.js').then(data=>{}).catch(err=>{})将基于acallback将函数转换为返回Promise函数的函数)=>{if(err){reject(err)}else{resolve(data)}})})}}readFilePromise=promisify(fs.readFile)writeFilePromise=promisify(fs.writeFile)statunlinkPromise=promisify(fs.stat)unlinkPromise=promisify(fs.unlink)将基于Promise的函数转换为返回回调函数的函数callbackify(promiseBased){返回函数(...args){varcb=args.pop()promiseBased(...args).then(val=>{cb(null,val)},reason=>{cb(reason)})}}当然,这两个函数已经集成在标准库中,即utilsvarfs=require('fs')varutils=require('utils')varreadFilePromise=utils.promisify(fs.readFile)varreadFile=utils.callbackify(readFilePromise)一个一个转还是有点麻烦。现在node已经提供了fs模块的promise版本fs=require('fs').promisesfs.readFile('a.txt').then().catch()实例接收文件夹路径并返回所有文件名在这个文件夹中。需要递归获取所有的文件名,放到一个一维数组中返回。需要写三个版本:同步版本callbackversionPromiseversion同步版本constfs=require('fs')constfsp=fs.promisesfunctionlistAllFilesSync(path){varstat=fs.statSync(path)varresult=[]if(stat.isFile()){return[path]//如果路径类型是文件,直接返回}else{varentries=fs.readdirsync(path,{withFileTypes:true})//读取所有文件,withFileTypes生成的数组将填充fs.Dirent对象,而不是字符串entries.forEach(entry=>{varfullPath=path+'/'+entry.name//新路径为原路径后接新文件夹名varfiles=listAllFilesSync(fullPath)//递归,将所有返回的数组推送到resultresult.push(...files)});返回结果}}console.log(listAllFilesSync('./'))PromiseversionfunctionlistAllFilesPromise(path){returnfsp.stat(path).then(stat=>{if(stat.isFile()){return[path]}else{returnfsp.readdir(path,{withFileTypes:true}).then(entries=>{returnPromise.all(entries.map(entry=>{returnlistAllFilesPromise(path+'/'+entry.name)})).then(arrays=>{return[].concat(...arrays)})})}})}listAllFilesPromise('./').then(console.log)异步版本函数listAllFilesCallback(path,callback){fs.stat(path,(err,stat)=>{if(stat.isFile()){callback([path])}else{fs.readdir(path,{withFileTypes:true},(err,entries)=>{varresult=[]varcount=0if(entries.length==0){callback([])//当file文件夹为空时直接返回而不是forEach}entries.forEach((entry,i)=>{varfullPath=path+'/'+entry.namelistAllFilesCallback(fullPath,(files)=>{result[i]=filescount++if(count==entries.length){callback([].concat(...result))}})})})}})}listAllFilesCallback('./',console.log)提升上面一共有三种方式:同步版时间效率低,promise版返回过多繁琐,异步版返回少但嵌套层次不灵活。有没有一种方法可以结合三者的优点呢?是的!将generator函数和promise函数结合,可以优化promise异步代码的编写。生成器函数简单回顾:生成器函数可以在执行过程中暂停,后面可以从暂停继续执行。使用function*声明next()方法返回一个对象,包含两个属性:value和done。value属性表示这次yield表达式的返回值。done属性是布尔类型,表示生成器函数是否执行完毕,在调用next()方法时返回。如果传入参数,那么这个参数会被传递给最后执行的yield语句左边的变量。当调用return时,生成器会立即变为完成状态,即done为真。如果return后面跟着一个值,那么这个值将被用作当前调用next()方法的返回值。function*natureNumber(n){for(vari=0;i
