在编程之前,一定要有一个信条:代码是为了减少工作量而生的,重复枯燥的工作是不能容忍的。
需求
这是由上篇文章衍生出的需求,我已经将一个文件夹内所有的文件名转译为英文,但我在页面上还需要将这些图片引入加载。如果一个一个手写到程序里,未免有点劳心劳神。那么何不将这个文件夹按照存放路径变成一个相应的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 + 'https://www.jb51.net/' + 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 {} })
对我来说,这段代码是一段丑陋的函数,处理方法过于粗糙,并且占用了不必要的内存空间,更严重的是,拖累了整体文件的运行速度,但时间仓促,加之能力菜的真实,暂时还尚未想出解决方案。