前端最根底的就是 HTML+CSS+Javascript。把握了这三门技术就算入门,但也仅仅是入门,当初前端开发的定义曾经远远不止这些。前端小课堂(HTML/CSS/JS),本着晋升技术水平,打牢基础知识的中心思想,咱们开课啦(每周四)。

应用 Node.js 很大一部分的应用场景是,写个小脚本批量解决一下反复无聊的工作。比如说:

  1. 获取所有 json 内容,而后过滤出想要的数据。
  2. 把所有图片压缩一下,而后放入压缩包中。
  3. 把字体文件提取压缩一下。
  4. 把所有文件重命名一下。

这些工作中咱们都须要拜访文件系统,之前在浏览器环境中其实是没有文件系统概念的,明天咱们来学一哈。

Node.js 文件模块

咱们能够应用 fs 来操作文件系统。

const fs = require('fs');

回调形式

所有的文件系统操作都具备同步的(10.x 有,历史也有)、回调的(10.x 有,历史也有)、以及基于 promise(12.x 开始就有了,历史没有) 的模式。

同步

同步的模式会阻止 Node.js 事件循环和进一步的 JavaScript 执行,直到操作实现
异样会被立刻地抛出,能够应用 try…catch 解决,也能够冒泡。

const fs = require('fs');try {  fs.unlinkSync('文件');  console.log('已胜利地删除文件');} catch (err) {  // 处理错误}

异步

异步的模式总是把实现回调作为其最初一个参数
传给实现回调的参数取决于具体方法,但第一个参数总是预留给异样。 如果操作被胜利地实现,则第一个参数会为 nullundefined

const fs = require('fs');fs.unlink('文件', (err) => {  if (err) throw err;  console.log('已胜利地删除文件');});

promise

基于 Promise 的操作会返回 Promise(当异步操作实现时会被解决)。

const fs = require('fs/promises');(async function(path) {  try {    await fs.unlink(path);    console.log(`已胜利地删除文件 ${path}`);  } catch (error) {    console.error('出错:', error.message);  }})('文件');

文件门路

反对 字符串(罕用Path库就对了)、Buffer(为了兼容)、URL(须要 new URL('file:///C:/' 为入参。能够用相对路径 (process.cwd() 能够查看以后门路),绝对路径。

字符串模式

在 Windows 上,Node.js 遵循独立驱动器工作目录的概念。 当应用没有反斜杠的驱动器门路时,能够察看到此行为。 例如, fs.readdirSync('C:\\') 可能会返回与 fs.readdirSync('C:') 不同的后果。 详见此 MSDN 页面。

个别咱们应用 Path 库:

  1. 能够间接看一下最终地址
  2. 能够正确应用以后零碎的门路分隔符,Unix零碎是"/",Windows零碎是""。

URL 模式

// 在 Windows 上:// - 带有主机名的 WHATWG 文件的 URL 会转换为 UNC 门路。// file://主机名/文件 => 主机名文件fs.readFileSync(new URL('file://主机名/文件'));// - 带有驱动器号的 WHATWG 文件的 URL 会转换为绝对路径。// file:///C:/文件 => C:文件fs.readFileSync(new URL('file:///C:/文件'));

API

目录 dir 类

fs.readdirSync() 同步获取目录
fs.readdir() 异步获取目录

当调用 fs.readdir()fs.readdirSync()withFileTypes 选项设置为 true)时,则生成的数组会应用 fs.Dirent 对象(而不是字符串或 Buffer)填充。

fs.mkdir(path[, options], callback) 异步地创立目录。回调会传入可能的异样、以及创立的第一个目录的门路(如果 recursivetrue), (err, [path])。可选的 options 参数能够是整数(指定 mode(权限和粘滞位))、或对象(具备 mode 属性和 recursive 属性(批示是否要创立父目录))。 当 path 是已存在的目录时,调用 fs.mkdir() 仅在 recursive 为 false 时才会导致谬误。
fs.mkdirSync(path[, options]) 同步地创立目录。 返回 undefined,或创立的第一个目录的门路(如果 recursivetrue)。 这是 fs.mkdir() 的同步版本。

监听 fs.FSWatcher 类

fs.watch() 每当指定监督的文件被批改时,会触发 'change' 事件。

// 应用 fs.watch()监听器的示例。fs.watch('./tmp', {         encoding: 'buffer'     }, (eventType, filename) => {  if (filename) {    console.log(filename);  }});

读取流 fs.ReadStream 类

应用 fs.createReadStream() 函数创立并返回的 fs.ReadStream 实例。

fs.createReadStream(path[, options])

  • path <string> | <Buffer> | <URL>
  • options <string> | <Object>

    • flags <string> 参见文件系统 flag 的反对。 默认值: 'r'
    • encoding <string> 默认值: null
    • fd <integer> 默认值: null
    • mode <integer> 默认值: 0o666
    • autoClose <boolean> 默认值: true
    • emitClose <boolean> 默认值: false
    • start <integer>
    • end <integer> 默认值: Infinity
    • highWaterMark <integer> 默认值: 64 * 1024
    • fs <Object> | <null> 默认值: null
  • 返回: <fs.ReadStream> 参见可读流。

写入流 fs.WriteStream 类

应用 fs.createWriteStream() 函数创立并返回的 fs.WriteStream 实例。

fs.createWriteStream(path[, options])

  • path <string> | <Buffer> | <URL>
  • options <string> | <Object>

    • flags <string> 参见文件系统 flag 的反对。 默认值: 'w'
    • encoding <string> 默认值: 'utf8'
    • fd <integer> 默认值: null
    • mode <integer> 默认值: 0o666
    • autoClose <boolean> 默认值: true
    • emitClose <boolean> 默认值: false
    • start <integer>
    • fs <Object> | <null> 默认值: null
  • 返回: <fs.WriteStream> 参见可写流。

,.

文件信息 fs.Stats 类

fs.Stats 对象提供了对于文件的信息。在遍历的时候咱们会判断文件类型,如果是文件就关上,如果是目录就递归

fs.stat()fs.lstat()fs.fstat()、以及它们的同步办法返回的对象都是此类型。 如果传给这些办法的 options 中的 bigint 为 true,则数值会是 bigint 型而不是 number 型,并且该对象还会蕴含额定的纳秒级精度的属性(以 Ns 为后缀)。

  1. stats.isDirectory() 如果 fs.Stats 对象形容文件系统目录,则返回 true
  2. stats.isFile() 如果 fs.Stats 对象形容一般的文件,则返回 true
  3. stats.size 文件的大小(以字节为单位)。
  4. stats.mtime 表明上次批改此文件的工夫戳。
    stats.mtimeMs 表明上次批改此文件的工夫戳,以 POSIX 纪元以来的毫秒数示意。
  5. stats.birthtime 示意此文件的创立工夫的工夫戳。
    stats.birthtimeMs 表明此文件的创立工夫的工夫戳,以 POSIX 纪元以来的毫秒数示意。

文件属性的工夫值#

中英对照

atimeMsmtimeMsctimeMsbirthtimeMs 属性是保留相应工夫(以毫秒为单位)的数值。 它们的精度取决于平台。 当将 bigint: true 传给生成该对象的办法时,属性将会是 bigint 型,否则它们将会是数字型。

atimeNsmtimeNsctimeNsbirthtimeNs 属性是保留相应工夫(以纳秒为单位)的 bigint。 仅当将 bigint: true 传给生成该对象的办法时,它们才会呈现。 它们的精度取决于平台。

atimemtimectimebirthtime 是对应工夫的 Date 对象。 Date 值和数值没有关联性。 赋值新的数值、或者扭转 Date 的值,都将不会影响到对应的属性。

stat 对象中的工夫具备以下语义:

  • atime "拜访工夫" - 上次访问文件数据的工夫。由 mknod(2)utimes(2)read(2) 零碎调用更改。
  • mtime "批改工夫" - 上次批改文件数据的工夫。由 mknod(2)utimes(2)write(2) 零碎调用更改。
  • ctime "更改工夫" - 上次更改文件状态(批改索引节点数据)的工夫。由 chmod(2)chown(2)link(2)mknod(2)rename(2)unlink(2)utimes(2)read(2)write(2) 零碎调用更改。
  • birthtime "创立工夫" - 创立文件的工夫。当创立文件时设置一次。 在不反对创立工夫的文件系统上,该字段可能改为保留 ctime1970-01-01T00:00Z(即 Unix 纪元工夫戳 0)。 在这种状况下,该值可能大于 atimemtime。 在 Darwin 和其余的 FreeBSD 衍生零碎上,也可能应用 utimes(2) 零碎调用将 atime 显式地设置为比 birthtime 更早的值。

在 Node.js 0.12 之前,在 Windows 零碎上 ctime 保留 birthtime。 从 0.12 开始, ctime 不再是“创立工夫”,而在 Unix 零碎上则素来都不是。

获取权限拜访 fs.access、fs.accessSync

除了判断权限,还能够判断是否存在。不过咱们个别能够间接 fs.open() 间接去解决 err 。通常,仅在不间接应用文件时(例如当其可拜访性是来自其余过程的信号时),才查看文件的可拜访性。

在 Windows 上,目录上的拜访控制策略(ACL)可能会限度对文件或目录的拜访。 然而, fs.access() 函数不查看 ACL,因而即便 ACL 限度用户读取或写入,也可能报告门路是可拜访的。

fs.access(path[, mode], callback)

  • path <string> | <Buffer> | <URL>
  • mode <integer> 默认值: fs.constants.F_OK
  • callback <Function>

    • err <Error>

测试用户对 path 指定的文件或目录的权限。 mode 参数是一个可选的整数,指定要执行的可拜访性查看。 查看文件可拜访性的常量理解 mode 的可选值。 能够创立由两个或更多个值按位或组成的掩码(例如 fs.constants.W_OK | fs.constants.R_OK)。

文件

  1. fs.open(path[, flags[, mode]], callback) 异步地关上文件。
    fs.openSync(path[, flags, mode]) 同步关上文件
  2. fs.read(fd, buffer, offset, length, position, callback)fd 指定的文件中读取数据,写入到 buffer 中。
    fs.readSync(fd, buffer, offset, length, position) 同步版本,从指定 fs 中读取数据
    fs.readFile(path[, options], callback) 异步地读取文件的全部内容。
    fs.readFileSync(path[, options]) 同步版本,读取文件全部内容
  3. fs.appendFile(path, data[, options], callback) 异步地追加数据到文件,如果文件尚不存在则创立文件。 data 能够是字符串或 Buffer
    fs.appendFileSync(path, data[, options]) 同步地将数据追加到文件,如果文件尚不存在则创立该文件。 data 能够是字符串或 Buffer

    // 间接针对文件追加fs.appendFileSync('文件.txt', '追加的数据', 'utf8');// 先关上文件,而后针对文件描述符追加fd = fs.openSync('文件.txt', 'a');fs.appendFileSync(fd, '追加的数据', 'utf8');
  4. fs.copyFile(src, dest[, mode], callback) 异步地将 src 拷贝到 dest。 默认状况下,如果 dest 曾经存在,则笼罩它。 除了可能的异样,回调函数没有其余参数。 Node.js 不保障拷贝操作的原子性。 如果在关上指标文件用于写入后产生谬误,则 Node.js 将尝试删除指标文件。
    fs.copyFileSync(src, dest[, mode]) 同步
  5. fs.rename(oldPath, newPath, callback) 异步地把 oldPath 文件重命名为 newPath 提供的路径名。 如果 newPath 已存在,则笼罩它。 除了可能的异样,实现回调没有其余参数。
    fs.renameSync(oldPath, newPath) 同步版本,文件重命名
  6. fs.write(fd, buffer[, offset[, length[, position]]], callback) 写入 bufferfd 指定的文件。不期待回调就对同一个文件屡次应用 fs.write()不平安的。 对于这种状况,倡议应用 fs.createWriteStream()

示例

获取目录下所有图片,并上传到服务器

const FormData = require('form-data');const fetch = require('node-fetch');var fs = require('fs');var path = require('path');var dirPath = 'cdn-transform'var filePath = path.resolve(`./node-upload-img/${dirPath}`);//文件遍历办法function fileDisplay(filePath){    fs.readdir(filePath,function(err,files){        if(err){            console.warn(err)        }else{            files.forEach(function(filename){                var filedir = path.join(filePath, filename);                var buffer = fs.readFileSync(filedir)                var formData = new FormData();                var fileName = `${filename}`                var url =  `www.lilnong.top/upload/fe-up/${dirPath}/${fileName}`;                formData.append('file', buffer, `${fileName}`);                fetch('http://www.lilnong.top/upload',{                    headers: formData.getHeaders(),                    method: 'post',                    body: formData                })                .then(v=>v.text())                .then(v=>{                    console.log('http://www.lilnong.top/upload', url, v)                })            })        }    });}fileDisplay(filePath)

递归遍历所有json

const readDir = (entry, paths = []) => {    const dirInfo = fs.readdirSync(entry);    dirInfo.forEach(item=>{        const location = path.join(entry,item);        const info = fs.statSync(location);        if(info.isDirectory()){            console.log(`dir:${location}`);            readDir(location, [item]);        }else{            if(/.json$/.test(location)){                readFile(location, paths)            }        }    })}console.log('__dirname', __dirname)readDir(__dirname);function readFile(path, pathKey){    return console.log(path, pathKey);})

微信公众号:前端linong

参考文献

  1. 前端培训目录、前端培训布局、前端培训打算
  2. http://nodejs.cn/api/fs.html#fs_file_paths