Converter uma estrutura de diretórios no sistema de arquivos para JSON com o Node.js

Eu tenho uma estrutura de arquivos como esta:

root |_ fruits |___ apple |______images |________ apple001.jpg |________ apple002.jpg |_ animals |___ cat |______images |________ cat001.jpg |________ cat002.jpg 

Eu gostaria de, usando Javascript e Node.js, ouvir este diretório raiz e todos os subdiretórios e criar um JSON que espelhe essa estrutura de diretório, cada nó contém tipo, nome, caminho e filhos:

 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/cat/images/cat001.jpg" }, { type: "file", name: "cat001.jpg", path: "/animals/cat/images/cat002.jpg" } ] } ] } ] } ]; 

Aqui está um JSON de café:

 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/cat/images/cat001.jpg" , type: "file" name: "cat001.jpg" path: "/animals/cat/images/cat002.jpg" ] ] ] ] 

como obter este formato de dados json em views django? (python)

Aqui está um esboço. O tratamento de erros é deixado como um exercício para o leitor.

 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)); } 

A resposta aceita funciona, mas é síncrona e prejudicará profundamente seu desempenho, especialmente em trees de diretórios grandes.
Eu recomendo fortemente que você use a seguinte solução assíncrona , é mais rápida e não bloqueia.
Baseado na solução paralela aqui .

 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); } }); }); }); }; 

Exemplo de uso:

 var dirTree = ('/path/to/dir'); diretoryTreeToObj(dirTree, function(err, res){ if(err) console.error(err); console.log(JSON.stringify(res)); }); 

há um módulo NPM para isso

https://www.npmjs.com/package/directory-tree

Cria um object representando uma tree de diretórios.

A partir de:

 photos ├── summer │ └── june │ └── windsurf.jpg └── winter └── january ├── ski.png └── snowboard.jpg 

Para:

 { "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" } ] } ] } ] } 

Uso

 var tree = directoryTree('/some/path'); 

E você também pode filtrar por extensões:

 var filteredTree = directoryTree('/some/path', ['.jpg', '.png']); 

Meu exemplo de CS (w / express) baseado na solução da Miika:

 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 

Você pode usar o código deste projeto, mas deve adaptar o código às suas necessidades:

https://github.com/NHQ/Node-FileUtils/blob/master/src/file-utils.js#L511-L593

A partir de:

 a |- b | |- c | | |- c1.txt | | | |- b1.txt | |- b2.txt | |- d | | | |- a1.txt |- a2.txt 

Para:

 { 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" } 

Fazendo:

 new File ("a").list (function (error, files){ //files... }); 

Eu usei ‘walk’ lib neste caso, ele obtém o caminho da raiz e percorre os arquivos e os diretórios recursivamente e emite um evento de diretório / arquivo com todas as informações necessárias de um nó, verifique essa implementação ->

 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); }); 

Aqui está uma solução assíncrona:

  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); } 

Observe que, quando um diretório não existe, um resultado vazio é retornado, em vez de um erro ser lançado.

Aqui está um resultado de amostra:

 { "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 } ] } ] } 

qual será:

 root |__ book.txt |__ cheatsheet-a5.pdf |__ docs |__ README.md