Javascript 使用Node.js将文件系统中的目录结构转换为JSON
我的文件结构如下:Javascript 使用Node.js将文件系统中的目录结构转换为JSON,javascript,node.js,d3.js,filesystems,Javascript,Node.js,D3.js,Filesystems,我的文件结构如下: root |_ fruits |___ apple |______images |________ apple001.jpg |________ apple002.jpg |_ animals |___ cat |______images |________ cat001.jpg |________ cat002.jpg 我想使用Javascript和Node.js,监听这个根目录和所有子目录,并创建一个JSON来镜像这个目录结构,每个节点都包含类型、名称、路径和子目录:
root
|_ fruits
|___ apple
|______images
|________ apple001.jpg
|________ apple002.jpg
|_ animals
|___ cat
|______images
|________ cat001.jpg
|________ cat002.jpg
我想使用Javascript和Node.js,监听这个根目录和所有子目录,并创建一个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/cat/images/cat001.jpg"
}, {
type: "file",
name: "cat001.jpg",
path: "/animals/cat/images/cat002.jpg"
}
]
}
]
}
]
}
];
下面是一个coffeescript 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/cat/images/cat001.jpg"
,
type: "file"
name: "cat001.jpg"
path: "/animals/cat/images/cat002.jpg"
]
]
]
]
如何在django视图中获取此json数据格式?(python)这里是一个草图。错误处理留给读者作为练习
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));
}
您可以使用此项目中的代码,但应根据需要调整代码: 发件人: 致: 做:
new File ("a").list (function (error, files){
//files...
});
我的CS示例(w/express)基于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
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);
}
公认的答案是有效的,但它是同步的,会严重影响您的性能,尤其是对于大型目录树。
我强烈建议您使用以下异步解决方案,它既快速又无阻塞。
基于并行求解 用法示例:
var dirTree = ('/path/to/dir');
diretoryTreeToObj(dirTree, function(err, res){
if(err)
console.error(err);
console.log(JSON.stringify(res));
});
它有一个NPM模块 创建表示目录树的对象 发件人: 致: 用法 您还可以按扩展进行筛选:
var filteredTree = directoryTree('/some/path', ['.jpg', '.png']);
在本例中,我使用了“walk”lib,它获取您的根路径,递归地遍历文件和目录,并发出一个directory/file事件,其中包含您从节点需要的所有信息, 检查该实现-->
以下是一个异步解决方案:
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
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
这在第1层非常有效,但是,子对象看起来像:children:[Object]。。。你看到什么问题了吗?是的。创建的对象很好,但默认情况下,console.log仅打印有限深度的对象。我编辑了代码来打印完整的树。谢谢你的func。我想最好使用path.join而不是
a+'/'+b
<代码>返回目录树(path.join(filename,child))代码>如何对输出进行排序,使目录先出现(按字母顺序),然后是文件(也按字母顺序)?@peterButcher如果以树结构打印,您会如何排序?但是您可以使用lodash来构造返回的对象。。它只是一个普通对象,所以请像对其他对象一样对其进行排序:)我从github中删除了该项目。这个链接是一个叉。@Gabriellamas为什么要从github中删除它?对我来说,这似乎是一个相当有用的项目。这是获取d3.js层次数据的常见要求。我想用d3.js标记这个问题,但堆栈溢出最多允许5个:(我希望这些答案中的一个能够从stdin中读取路径,这样你就可以将路径列表转换成json对象,比如:find | paths2json
。这将通过管道充分利用Unix的可组合性。这正是我所需要的。谢谢。它工作得很好。小尼:你在diretorytreetobj中有一个输入错误,我想nk应该是directoryTreeToObj否?
var filteredTree = directoryTree('/some/path', ['.jpg', '.png']);
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);
});
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