NodeJS 将文件夹按照存放路径变成一个对应的JSON的方法

2018-10-17 10 维伦

在编程之前,一定要有一个信条:代码是为了减少工作量而生的,重复枯燥的工作是不能容忍的。

需求

这是由上篇文章衍生出的需求,我已经将一个文件夹内所有的文件名转译为英文,但我在页面上还需要将这些图片引入加载。如果一个一个手写到程序里,未免有点劳心劳神。那么何不将这个文件夹按照存放路径变成一个相应的JSON在前端请求并渲染呢?

说干就干。

文件夹的路径如下所示:

一级路径:test
二级路径:A,B,C...共十三个文件夹
三级路径:每个二级路径下有3-8个文件夹不等
四级路径:a.jpg,b.jpg,c.jpg等数量不等的图片文件

思路

1.一个全局变量:数组类型obj。

2.递归遍历目录,是文件夹的取出名字,建立一个对象,放入文件夹名name和一个空数组list,如

{
  name:'fileName',
  list:[]//用来存放下属路径
}

3.依次重复步骤2,直至寻找到路径下的文件,将文件名name和拼合好的路径src放入归属的list中,如下:

list:[
  {
    name:'fileName',
    src:'filePath'
  }
]

实践

语言

NodeJS

需求模块
fs/async。

代码 先上代码,逻辑解析在提示补上。

(function() {
 Array.prototype.distinct = function() {
 var arr = this,
  result = [],
  i,
  j,
  len = arr.length;
 for (i = 0; i < len; i++) {
  for (j = i + 1; j < len; j++) {
  if (arr[i].name === arr[j].name) {
   j = ++i;
  }
  }
  result.push(arr[i]);
 }
 return result;
 }
 // 主业务代码
 var fs = require("fs");
 var async = require('async');
 var obj = [];
 var search = function(src) {
 // 读取目录中的所有文件/目录
 fs.readdir(src, function(err, paths) {
  if (err) {
  throw err;
  }
  paths.forEach(function(path) {
  var _src = src + '/' + path;
  fs.stat(_src, function(err, st) {
   if (err) {
   throw err;
   }
   // 判断是否为文件
   if (st.isFile()) {
   async.forEachLimit(obj, 1000, function(item, callback) {
    if (src.match(new RegExp(item.name))) {

    item.list.forEach(function(iv) {
     if (src.match(new RegExp(iv.name))) {
     iv.list.push({
      name: path.split('.')[0],
      src: _src
     })

     }

    })
    }
    callback(null, item)
   })
   obj = obj.distinct();
   fs.writeFile("main.json", JSON.stringify(obj), "utf-8", (error) => {
    //监听错误,如正常输出,则打印null
    if (error == null) {
    obj = null;

    }

   });
   }
   // 如果是目录则递归调用自身
   else if (st.isDirectory()) {
   if (obj.length <= 0) {
    obj.push({
    name: path,
    list: []
    })
   } else {
    var value = paths;
    async.forEachLimit(obj, 1000, function(item, callback) {
    //如果数组里已有
    if (src.match(new RegExp(item.name))) {
     item.list.push({
     name: path,
     list: []
     })
    } else {
     value.forEach(function(vv) {
     if (vv == item.name) {
      obj.push({
      name: path,
      list: []
      })
     } else {}
     })
    }
    callback(null, item)
    }, function(err) {
    if (err) throw err;
    })
   }
   exists(_src, search);

   }
  });
  });
 });
 };
 var exists = function(src, callback) {
 callback(src);
 };
 exists('./test2', search);
})();

提示

首先,要有心理准备,这个轮子里使用了递归+很多遍历,这对于内存占用是很严重的,尤其是我们还有一个 全局变量OBJ ,如果逻辑处理不好很容易会出现错误内存溢出。这也是为什么我引入了async模块的理由。

在引入模块之后,第一步要着眼于判断为文件夹后的函数:

//如果obj为空,说明我们是第一次进入函数,那么插入一个新的元素
if (obj.length <= 0) {
 obj.push({
 name: path,
 list: []
 })
}

async.forEachLimit是异步串行函数,可以同批次运行指定数量的代码,可以避免一次遍历太多,里面的参数依次为(数组,指定的数量,执行函数,错误回调)。

下面代码中的参数很多,如paths,path,src,_src等,打代码的时候经常会让我头脑不清晰,这也侧面告诫了我一个问题,同一作用域内不宜有太多处理函数。

//如果路径参数src里匹配到了obj中元素的name,说明数组里已经有了上级文件夹,则向此上级文件夹内插入此时的文件夹名path
if (src.match(new RegExp(item.name))) {
 item.list.push({
 name: path,
 list: []
 })
} 

/*如果没有上级文件夹,那往尾部插入新的元素
 *进行一次遍历排查,去除掉二级路径向尾部插入的举动*/

value.forEach(function(vv) {
 if (vv == item.name) {
 obj.push({
  name: path,
  list: []
 })
 } else {}
})

对我来说,这段代码是一段丑陋的函数,处理方法过于粗糙,并且占用了不必要的内存空间,更严重的是,拖累了整体文件的运行速度,但时间仓促,加之能力菜的真实,暂时还尚未想出解决方案。

还要备注的是,这里本来想达到的去重判断因为上级方法套用的遍历次序不同,造成我只完成了初级的防止push重复,却又有了递归式的重复。有兴趣的朋友在使用这个demo时可以将首行的去重方法删除,看看效果。

处理好了文件夹状态下的方法,来看看已经到了四级路径的处理

//src是此时运行的search方法内的文件路径参数,对item.name也就是二级路径的文件名进行匹配,如果正确,则对三级路径下的文件名进行匹配(本来我想在这里写递归或者回调的,太懒取消原计划)。全部都匹配正确,则往内插入文件名和文件路径
async.forEachLimit(obj, 1000, function(item, callback) {
 if (src.match(new RegExp(item.name))) {
 item.list.forEach(function(iv) {
  if (src.match(new RegExp(iv.name))) {
  iv.list.push({
   name: path.split('.')[0],
   src: _src
  })
  }
 })
 }
 callback(null, item)
})

函数到这里时,就是最后一步,写入json文件了,不能忘记的是,这里需要进行一次去重,原因在上文提到了,方法在函数行首。

PS:在运行结束之后,一定要将全局变量清空,虽然我暂时尚未遇到因此理由内存的崩溃,但可以预见到的是,在用于庞大文件夹时,势必会对内存有很大的伤害。

obj = obj.distinct();
fs.writeFile("main.json", JSON.stringify(obj), "utf-8", (error) => {
 //监听错误,如正常输出,则打印null
 if (error == null) {
 obj = null;
 }
});

END

这篇小笔记就结束了,这个轮子遇到的问题很浅显,但值得重视。对我将来写大型单页应用或是系统型网站非常有借鉴意义,所以此篇文章的漏洞和错误,我会再次进行排查,以达到最优的解决方法。我在这里抛砖引玉,诚恳希望各位同仁前辈能不吝赐教!

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

展开阅读全文

更多Javascript文章

使用jquery.upload.js实现异步上传示例代码
Jul 29 28
node.js中的fs.fchownSync方法使用说明
Dec 16 20
JQuery显示隐藏页面元素的方法总结
Apr 16 30
正则中的回溯定义与用法分析【JS与java实现】
Dec 27 101
基于代数方程库Algebra.js解二元一次方程功能示例
Jun 09 13
vuex实现登录状态的存储,未登录状态不允许浏览的方法
Mar 09 53
JS实现可用滑块滑动的缓动图代码
Sep 01 21
手机访问当前页面