【Node】遍历目录文件,生成 JSON(仅获取文件、非递归)

一、几种实现(readfile.js)

1. 同步版(阻塞执行)

// 导入模块
const path = require('path');
const fs = require('fs');

const pathName = './';  // 需遍历的文件夹路径
const jsonPath = './files.json';  // 生成 json 的文件路径

// 打开/创建文件并写入 JSON
function writeJSON(file, data) {
  // 同步写入
  fs.writeFileSync(file, JSON.stringify(data, '', '\t'));
}

// 同步操作
((p, j) => {
  try {
    // 用于存储年份
    const years = [];

    // 得到想要的数据,作为原材料(未过滤)
    const lists = fs.readdirSync(p).map(file => {
      // 初始化,加工排序后再给值
      const id = null;
      // 文件名
      const fileName = file;
      // 提取文件扩展名
      const extension = file.substring(file.lastIndexOf('.') + 1, file.length);
      // 将获取的stats对象,解构
      let {size, atime, mtime, ctime, birthtime} = fs.statSync(path.join(p, file));
      // 文件大小转为 kb 单位,向下取整,小于等于 1kb 均为 1kb
      size = `${Math.floor(size / 1000) > 1 ? Math.floor(size / 1000) : 1} kb`;
      // 返回需要的数据对象
      return {
        id,
        extension,  // 扩展名
        fileName, // 文件名
        size, // 文件大小
        atime,  // 最近访问文件时间
        mtime,  // 最近修改内容时间
        ctime, // 最近改变状态时间
        birthtime // 文件创建时间
      }
    });

    // 加工1(先升序给id)
    lists.sort((a, b) => {
      // (根据情况,选取你要排序的一种时间)
      return a.mtime - b.mtime;
    }).forEach((item, i) => {
      // 少于两位自动补零
      item.id = `${i+1}`.padStart(2,'0');
    });

    // 加工2(再降序获取年份)
      // 看情况,项目需要JSON倒序就加这步,否则就在上面写
    lists.sort((a, b) => {
      return b.mtime - a.mtime;
    }).forEach(({atime, mtime, ctime, birthtime}) => {
      const y = mtime.getFullYear();
      years.indexOf(y) < 0 ? years.push(y) : '<-- 避免重复';
    });
    
    // 成品(年份分组)
    const dirs = years.map(year => {
      // 过滤
      const files = lists.filter(item => {
        // 根据情况,选取你要排序的一种时间
        const {atime, mtime, ctime, birthtime} = item;
        // 过滤出年份符合的项
        if (mtime.getFullYear() === year) return item;
      })
      // 返回对象作为数组的元素项
      return {year, files};
    });

    // 生成 json
    writeJSON(j, dirs);

    // 由上而下,错误会阻塞,成功就会执行到此步
    console.log('操作成功!');
  } catch (err) {
    console.error(err);
  }
})(pathName, jsonPath);

2. 异步版(回调地狱)

// 导入模块
const path = require('path');
const fs = require('fs');

const pathName = './';  // 需遍历的文件夹路径
const jsonPath = './files.json';  // 生成 json 的文件路径

// 打开/创建文件并写入 JSON
function writeJSON(file, data) {
  fs.writeFile(file, JSON.stringify(data, '', '\t'), err => {
    err ? console.error(err) : console.log('操作成功!');
  });
}

// 异步操作(回调地狱)
((p, j) => {
  // 只能在异步回调内读取,外部读取是同步操作,所以空值
  const lists = []; // 存储原料
  const years = [];  // 用于存储年份

  // 异步遍历目录
  fs.readdir(p, (err, fileArr) => {
    if (err) throw err;

    // 遍历数组
    fileArr.forEach(file => {
      // 异步获取每个文件状态信息
      fs.stat(path.join(p, file), (err, stats) => {
        if (err) throw err;
        
        if (stats.isFile()) {
          // 初始化,加工排序后再给值
          const id = null;
          // 文件名
          const fileName = file;
          // 提取文件扩展名
          const extension = file.substring(file.lastIndexOf('.') + 1, file.length);
          // 解构
          let {size, atime, mtime, ctime, birthtime} = stats;
          // 文件大小转为 kb 单位,向下取整,小于等于 1kb 均为 1kb
          size = `${Math.floor(size / 1000) > 1 ? Math.floor(size / 1000) : 1} kb`;

          // 填充数组
          lists.push({
            id,
            extension,  // 扩展名
            fileName, // 文件名
            size, // 文件大小
            atime,  // 最近访问文件时间
            mtime,  // 最近修改内容时间
            ctime, // 最近改变状态时间
            birthtime // 文件创建时间
          });
        }

        // 遍历结束,开始干活
        if (lists.length === fileArr.length) {
          // 加工1(先升序给id)
          lists.sort((a, b) => {
            // (根据情况,选取你要排序的一种时间)
            return a.mtime - b.mtime;
          }).forEach((item, i) => {
            // 少于两位自动补零
            item.id = `${i+1}`.padStart(2,'0');
          });

          // 加工2(再降序获取年份)
            // 看情况,项目需要JSON倒序就加这步,否则就在上面写
          lists.sort((a, b) => {
            return b.mtime - a.mtime;
          }).forEach(({atime, mtime, ctime, birthtime}) => {
            const y = mtime.getFullYear();
            years.indexOf(y) < 0 ? years.push(y) : '<-- 避免重复';
          });
          
          // 成品(年份分组)
          const dirs = years.map(year => {
            // 过滤
            const files = lists.filter(item => {
              // 根据情况,选取你要排序的一种时间
              const {atime, mtime, ctime, birthtime} = item;
              // 过滤出年份符合的项
              if (mtime.getFullYear() === year) return item;
            })
            // 返回对象作为数组的元素项
            return {year, files};
          })

          // 生成 json
          writeJSON(j, dirs);
        }
      });
    });
  });
})(pathName, jsonPath);

3. promise 版(链式操作)

// 导入模块
const path = require('path');
const fs = require('fs/promises');

const pathName = './';  // 需遍历的文件夹路径
const jsonPath = './files.json';  // 生成 json 的文件路径

// 获取原料
function getLists(p, arr) {
  // 通过 all 将结果集一并传递给下一个 then(),【即作为 resolve()】
  return Promise.all(arr.map(file => {
    // 每个文件的信息组成的数组
    return fs.stat(path.join(p, file)).then(stats => {
      if (stats.isFile) {
        // 初始化,加工排序后再给值
        const id = null;
        // 文件名
        const fileName = file;
        // 提取文件扩展名
        const extension = file.substring(file.lastIndexOf('.') + 1, file.length);
        // 将获取的stats对象,解构
        let {size, atime, mtime, ctime, birthtime} = stats;
        // 文件大小转为 kb 单位,向下取整,小于等于 1kb 均为 1kb
        size = `${Math.floor(size / 1000) > 1 ? Math.floor(size / 1000) : 1} kb`;
        // 返回需要的数据对象
        return {
          id,
          extension,  // 扩展名
          fileName, // 文件名
          size, // 文件大小
          atime,  // 最近访问文件时间
          mtime,  // 最近修改内容时间
          ctime, // 最近改变状态时间
          birthtime // 文件创建时间
        }
      }
    });
  }));
}

// 原料加工,得到成品
function getDirs(arr) {
  // 其实此处可以不用 promise 函数,直接 return 数组结果就行了
  return new Promise((resolve, reject) => {
    // 用于存储年份
    const years = [];

    // 加工1(先升序给id)
    arr.sort((a, b) => {
      // (根据情况,选取你要排序的一种时间)
      return a.mtime - b.mtime;
    }).forEach((item, i) => {
      // 少于两位自动补零
      item.id = `${i+1}`.padStart(2,'0');
    });

    // 加工2(再降序获取年份)
      // 看情况,项目需要JSON倒序就加这步,否则就在上面写
    arr.sort((a, b) => {
      return b.mtime - a.mtime;
    }).forEach(({atime, mtime, ctime, birthtime}) => {
      const y = mtime.getFullYear();
      years.indexOf(y) < 0 ? years.push(y) : '<-- 避免重复';
    });

    // 成品(年份分组)
    resolve(years.map(year => {
      // 过滤
      const files = arr.filter(item => {
        // 根据情况,选取你要排序的一种时间
        const {atime, mtime, ctime, birthtime} = item;
        // 过滤出年份符合的项
        if (mtime.getFullYear() === year) return item;
      })
      // 返回对象作为数组的元素项
      return {year, files};
    }));
  });
}

// 打开/创建文件并写入 JSON
function writeJSON(file, data) {
  // 异步
  return fs.writeFile(file, JSON.stringify(data, '', '\t'));
}

// promise
((p, j) => {
  fs.readdir(p) // 遍历目录
  .then(files => getLists(p, files)) // 获取原料
  .then(lists => getDirs(lists))  // 加工完成
  .then(data => writeJSON(j, data))  // 出产
  .then(suc => console.log('操作成功!')) // 成功
  .catch(err => console.error(err));  // 失败
})(pathName, jsonPath);

4. async / await 版(异步改同步)

// 导入模块
const path = require('path');
const fs = require('fs/promises');
const { stat } = require('fs');

const pathName = './';  // 需遍历的文件夹路径
const jsonPath = './files.json';  // 生成 json 的文件路径

// 打开/创建文件并写入 JSON
function writeJSON(file, data) {
  // 异步写入(但 await 改为了同步,所以错误回调不需要了)
  fs.writeFile(file, JSON.stringify(data, '', '\t'));
}

// async/await 将异步改为了同步写法
(async (p, j) => {
  try {
    // 用于存储年份
    const years = [];

    // 得到每个原材料的 promise 对象组成的数组【注意map回调需要加async关键字】
    const promiseArr = (await fs.readdir(p)).map(async file => {
      // 初始化,加工排序后再给值
      const id = null;
      // 文件名
      const fileName = file;
      // 提取文件扩展名
      const extension = file.substring(file.lastIndexOf('.') + 1, file.length);
      // 将获取的stats对象,解构
      let {size, atime, mtime, ctime, birthtime} = await fs.stat(path.join(p, file));
      // 文件大小转为 kb 单位,向下取整,小于等于 1kb 均为 1kb
      size = `${Math.floor(size / 1000) > 1 ? Math.floor(size / 1000) : 1} kb`;
      // 返回需要的数据对象
      return {
        id,
        extension,  // 扩展名
        fileName, // 文件名
        size, // 文件大小
        atime,  // 最近访问文件时间
        mtime,  // 最近修改内容时间
        ctime, // 最近改变状态时间
        birthtime // 文件创建时间
      };
    });

    // 通过 Promise.all 将所有 promise 转为真正的数组
    const lists = await Promise.all(promiseArr);
    
    // 加工1(先升序给id)
    lists.sort((a, b) => {
      // (根据情况,选取你要排序的一种时间)
      return a.mtime - b.mtime;
    }).forEach((item, i) => {
      // 少于两位自动补零
      item.id = `${i+1}`.padStart(2,'0');
    });

    // 加工2(再降序获取年份)
      // 看情况,项目需要JSON倒序就加这步,否则就在上面写
    lists.sort((a, b) => {
      return b.mtime - a.mtime;
    }).forEach(({atime, mtime, ctime, birthtime}) => {
      const y = mtime.getFullYear();
      years.indexOf(y) < 0 ? years.push(y) : '<-- 避免重复';
    });
    
    // 成品(年份分组)
    const dirs = years.map(year => {
      // 过滤
      const files = lists.filter(item => {
        // 根据情况,选取你要排序的一种时间
        const {atime, mtime, ctime, birthtime} = item;
        // 过滤出年份符合的项
        if (mtime.getFullYear() === year) return item;
      })
      // 返回对象作为数组的元素项
      return {year, files};
    });
    
    // 生成 json
    await writeJSON(j, dirs);

    // 已为同步操作,所以若通畅无阻,即成功
    console.log('操作成功!');
  } catch (err) {
    console.error(err);
  }
})(pathName, jsonPath);

二、示例(遍历当前文件夹,并在当前文件夹生成json文件)

1. 文件夹列表

在这里插入图片描述

2. Node.js 遍历并生成 JSON 文件

终端cd进入当前目录(readfile.js目录)输入:node readfile.js
在这里插入图片描述

3. JSON 结构(年份基于 mtime 倒序,id基于 mtime 正序)

在这里插入图片描述

4. 关于 Stats 对象属性

在这里插入图片描述


三、Tips(排序建议)

文件基于 mtime (最后修改内容时间)进行分组,如果需要基于 atime(最近访问文件时间) / ctime(最近更改状态时间)/ birthtime (文件创建时间),需在以下地方进行修改:

a. 同步版

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

b. 异步版

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

c. promise 版

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4. async / await 版

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值