使用Node.js将文件系统中的目录结构转换为JSON
我有这样的文件结构:
root |_ fruits |___ apple |______images |________ apple001.jpg |________ apple002.jpg |_ animals |___ cat |______images |________ cat001.jpg |________ cat002.jpg
我想使用Javascript和Node.js,听这个根目录和所有的子目录,并创build一个JSON镜像这个目录结构,每个节点包含types,名称,path和子项:
data = [ { type: "folder", name: "animals", path: "/animals", children: [ { type: "folder", name: "cat", path: "/animals/cat", children: [ { type: "folder", name: "images", path: "/animals/cat/images", children: [ { type: "file", name: "cat001.jpg", path: "/animals/cathttp://img.dovov.comcat001.jpg" }, { type: "file", name: "cat001.jpg", path: "/animals/cathttp://img.dovov.comcat002.jpg" } ] } ] } ] } ];
这是一个咖啡的JSON:
data = [ type: "folder" name: "animals" path: "/animals" children : [ type: "folder" name: "cat" path: "/animals/cat" children: [ type: "folder" name: "images" path: "/animals/cat/images" children: [ type: "file" name: "cat001.jpg" path: "/animals/cathttp://img.dovov.comcat001.jpg" , type: "file" name: "cat001.jpg" path: "/animals/cathttp://img.dovov.comcat002.jpg" ] ] ] ]
如何在Django视图中获取这个json数据格式?(python)
这是一个草图。 error handling是读者的一个练习。
var fs = require('fs'), path = require('path') function dirTree(filename) { var stats = fs.lstatSync(filename), info = { path: filename, name: path.basename(filename) }; if (stats.isDirectory()) { info.type = "folder"; info.children = fs.readdirSync(filename).map(function(child) { return dirTree(filename + '/' + child); }); } else { // Assuming it's a file. In real life it could be a symlink or // something else! info.type = "file"; } return info; } if (module.parent == undefined) { // node dirTree.js ~/foo/bar var util = require('util'); console.log(util.inspect(dirTree(process.argv[2]), false, null)); }
被接受的答案是有效的,但是它是同步的,并且会严重伤害你的performance,特别是对于大目录树。
我强烈build议你使用下面的asynchronous解决scheme,它既快又不堵塞。
基于这里的并行解决scheme。
var fs = require('fs'); var path = require('path'); var diretoryTreeToObj = function(dir, done) { var results = []; fs.readdir(dir, function(err, list) { if (err) return done(err); var pending = list.length; if (!pending) return done(null, {name: path.basename(dir), type: 'folder', children: results}); list.forEach(function(file) { file = path.resolve(dir, file); fs.stat(file, function(err, stat) { if (stat && stat.isDirectory()) { diretoryTreeToObj(file, function(err, res) { results.push({ name: path.basename(file), type: 'folder', children: res }); if (!--pending) done(null, results); }); } else { results.push({ type: 'file', name: path.basename(file) }); if (!--pending) done(null, results); } }); }); }); };
用法示例:
var dirTree = ('/path/to/dir'); diretoryTreeToObj(dirTree, function(err, res){ if(err) console.error(err); console.log(JSON.stringify(res)); });
有一个NPM模块
https://www.npmjs.com/package/directory-tree
创build表示目录树的对象。
从:
photos ├── summer │ └── june │ └── windsurf.jpg └── winter └── january ├── ski.png └── snowboard.jpg
至:
{ "path": "", "name": "photos", "type": "directory", "children": [ { "path": "summer", "name": "summer", "type": "directory", "children": [ { "path": "summer/june", "name": "june", "type": "directory", "children": [ { "path": "summer/june/windsurf.jpg", "name": "windsurf.jpg", "type": "file" } ] } ] }, { "path": "winter", "name": "winter", "type": "directory", "children": [ { "path": "winter/january", "name": "january", "type": "directory", "children": [ { "path": "winter/january/ski.png", "name": "ski.png", "type": "file" }, { "path": "winter/january/snowboard.jpg", "name": "snowboard.jpg", "type": "file" } ] } ] } ] }
用法
var tree = directoryTree('/some/path');
你也可以通过扩展来过滤:
var filteredTree = directoryTree('/some/path', ['.jpg', '.png']);
基于Miika解决scheme的我的CS例子(w / express):
fs = require 'fs' #file system module path = require 'path' # file path module # returns json tree of directory structure tree = (root) -> # clean trailing '/'(s) root = root.replace /\/+$/ , "" # extract tree ring if root exists if fs.existsSync root ring = fs.lstatSync root else return 'error: root does not exist' # type agnostic info info = path: root name: path.basename(root) # dir if ring.isDirectory() info.type = 'folder' # execute for each child and call tree recursively info.children = fs.readdirSync(root) .map (child) -> tree root + '/' + child # file else if ring.isFile() info.type = 'file' # link else if ring.isSymbolicLink() info.type = 'link' # other else info.type = 'unknown' # return tree info # error handling handle = (e) -> return 'uncaught exception...' exports.index = (req, res) -> try res.send tree './test/' catch e res.send handle e
你可以使用这个项目中的代码,但是你应该根据你的需要调整代码:
https://github.com/NHQ/Node-FileUtils/blob/master/src/file-utils.js#L511-L593
从:
a |- b | |- c | | |- c1.txt | | | |- b1.txt | |- b2.txt | |- d | | | |- a1.txt |- a2.txt
至:
{ b: { "b1.txt": "a/b/b1.txt", "b2.txt": "a/b/b2.txt", c: { "c1.txt": "a/b/c/c1.txt" } }, d: {}, "a2.txt": "a/a2.txt", "a1.txt": "a/a1.txt" }
这样做:
new File ("a").list (function (error, files){ //files... });
在这种情况下,我使用了'walk'lib,它获取根path,recursion地遍历文件和目录,并从节点发出一个包含所有需要的信息的目录/文件事件,检查实现 – >
const walk = require('walk'); class FsTree { constructor(){ } /** * @param rootPath * @returns {Promise} */ getFileSysTree(rootPath){ return new Promise((resolve, reject)=>{ const root = rootPath || __dirname; // if there's no rootPath use exec location const tree = []; const nodesMap = {}; const walker = walk.walk(root, { followLinks: false}); // filter doesn't work well function addNode(node, path){ if ( node.name.indexOf('.') === 0 || path.indexOf('/.') >= 0){ // ignore hidden files return; } var relativePath = path.replace(root,''); node.path = relativePath + '/' + node.name; nodesMap[node.path] = node; if ( relativePath.length === 0 ){ //is root tree.push(node); return; } node.parentPath = node.path.substring(0,node.path.lastIndexOf('/')); const parent = nodesMap[node.parentPath]; parent.children.push(node); } walker.on('directory', (path, stats, next)=>{ addNode({ name: stats.name, type:'dir',children:[]}, path); next(); }); walker.on('file', (path,stats,next)=>{ addNode({name:stats.name, type:'file'},path); next(); }); walker.on('end',()=>{ resolve(tree); }); walker.on('errors', (root, nodeStatsArray, next) => { reject(nodeStatsArray); next(); }); }); } } const fsTreeFetcher = new FsTree(); fsTreeFetcher.getFileSysTree(__dirname).then((result)=>{ console.log(result); });
这是一个asynchronous解决scheme:
function list(dir) { const walk = entry => { return new Promise((resolve, reject) => { fs.exists(entry, exists => { if (!exists) { return resolve({}); } return resolve(new Promise((resolve, reject) => { fs.lstat(entry, (err, stats) => { if (err) { return reject(err); } if (!stats.isDirectory()) { return resolve({ // path: entry, // type: 'file', name: path.basename(entry), time: stats.mtime, size: stats.size }); } resolve(new Promise((resolve, reject) => { fs.readdir(entry, (err, files) => { if (err) { return reject(err); } Promise.all(files.map(child => walk(path.join(entry, child)))).then(children => { resolve({ // path: entry, // type: 'folder', name: path.basename(entry), time: stats.mtime, entries: children }); }).catch(err => { reject(err); }); }); })); }); })); }); }); } return walk(dir); }
请注意,当一个目录不存在时,返回一个空的结果,而不是抛出一个错误。
这是一个示例结果:
{ "name": "root", "time": "2017-05-09T07:46:26.740Z", "entries": [ { "name": "book.txt", "time": "2017-05-09T07:24:18.673Z", "size": 0 }, { "name": "cheatsheet-a5.pdf", "time": "2017-05-09T07:24:18.674Z", "size": 262380 }, { "name": "docs", "time": "2017-05-09T07:47:39.507Z", "entries": [ { "name": "README.md", "time": "2017-05-08T10:02:09.651Z", "size": 19229 } ] } ] }
这将是:
root |__ book.txt |__ cheatsheet-a5.pdf |__ docs |__ README.md