node.js 读取目录中的所有文件,将它们存储在对象中,并发送对象

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10049557/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 15:28:11  来源:igfitidea点击:

Reading all files in a directory, store them in objects, and send the object

node.js

提问by Saif Bechan

I do not know if this is possible, but here goes. And working with callbacks makes it even more difficult.

我不知道这是否可能,但这里是。和回调一起工作使它变得更加困难。

I have a directory with html files that I want to send back to the client in Object chunks with node.js and socket.io.

我有一个包含 html 文件的目录,我想使用 node.js 和 socket.io 以对象块的形式将其发送回客户端。

All my files are in /tmpl

我所有的文件都在 /tmpl

So socket needs to read all the files in /tmpl.

所以socket需要读取/tmpl中的所有文件。

for each file it has to store the data in an object with the filename as the key, and the content as the value.

对于每个文件,它必须将数据存储在一个对象中,文件名作为键,内容作为值。

  var data;
  // this is wrong because it has to loop trough all files.
  fs.readFile(__dirname + '/tmpl/filename.html', 'utf8', function(err, html){
      if(err) throw err;
      //filename must be without .html at the end
      data['filename'] = html;
  });
  socket.emit('init', {data: data});

The final callback is also wrong. It has to be called when all the files in the directory are done.

最后的回调也是错误的。当目录中的所有文件都完成时,它必须被调用。

But I do not know how to create the code, anyone know if this is possibel?

但我不知道如何创建代码,有人知道这是否可行吗?

回答by stewe

So, there are three parts. Reading, storing and sending.

所以,分为三个部分。读取、存储和发送。

Here's the reading part:

以下是阅读部分:

var fs = require('fs');

function readFiles(dirname, onFileContent, onError) {
  fs.readdir(dirname, function(err, filenames) {
    if (err) {
      onError(err);
      return;
    }
    filenames.forEach(function(filename) {
      fs.readFile(dirname + filename, 'utf-8', function(err, content) {
        if (err) {
          onError(err);
          return;
        }
        onFileContent(filename, content);
      });
    });
  });
}

Here's the storing part:

这是存储部分:

var data = {};
readFiles('dirname/', function(filename, content) {
  data[filename] = content;
}, function(err) {
  throw err;
});

The sending part is up to you. You may want to send them one by one or after reading completion.

发送部分由您决定。您可能希望将它们一一发送或在阅读完成后发送。

If you want to send files after reading completion you should either use sync versions of fsfunctions or use promises. Async callbacks is not a good style.

如果您想在阅读完成后发送文件,您应该使用fs函数的同步版本或使用承诺。异步回调不是一个好的风格。

Additionally you asked about stripping an extension. You should proceed with questions one by one. Nobody will write a complete solution just for you.

此外,您询问了关于剥离扩展的问题。你应该一一进行问题。没有人会专门为您编写完整的解决方案。

回答by loretoparisi

This is a modern Promiseversion of the previous one, using a Promise.allapproach to resolve all promises when all files have been read:

这是上一个的现代Promise版本,使用一种Promise.all方法在读取所有文件时解决所有承诺:

/**
 * Promise all
 * @author Loreto Parisi (loretoparisi at gmail dot com)
 */
function promiseAllP(items, block) {
    var promises = [];
    items.forEach(function(item,index) {
        promises.push( function(item,i) {
            return new Promise(function(resolve, reject) {
                return block.apply(this,[item,index,resolve,reject]);
            });
        }(item,index))
    });
    return Promise.all(promises);
} //promiseAll

/**
 * read files
 * @param dirname string
 * @return Promise
 * @author Loreto Parisi (loretoparisi at gmail dot com)
 * @see http://stackoverflow.com/questions/10049557/reading-all-files-in-a-directory-store-them-in-objects-and-send-the-object
 */
function readFiles(dirname) {
    return new Promise((resolve, reject) => {
        fs.readdir(dirname, function(err, filenames) {
            if (err) return reject(err);
            promiseAllP(filenames,
            (filename,index,resolve,reject) =>  {
                fs.readFile(path.resolve(dirname, filename), 'utf-8', function(err, content) {
                    if (err) return reject(err);
                    return resolve({filename: filename, contents: content});
                });
            })
            .then(results => {
                return resolve(results);
            })
            .catch(error => {
                return reject(error);
            });
        });
  });
}

How to Use It:

如何使用它:

Just as simple as doing:

就像做一样简单:

readFiles( EMAIL_ROOT + '/' + folder)
.then(files => {
    console.log( "loaded ", files.length );
    files.forEach( (item, index) => {
        console.log( "item",index, "size ", item.contents.length);
    });
})
.catch( error => {
    console.log( error );
});

Supposed that you have another list of folders you can as well iterate over this list, since the internal promise.all will resolve each of then asynchronously:

假设您有另一个文件夹列表,您也可以遍历此列表,因为内部 promise.all 将异步解析每个文件夹:

var folders=['spam','ham'];
folders.forEach( folder => {
    readFiles( EMAIL_ROOT + '/' + folder)
    .then(files => {
        console.log( "loaded ", files.length );
        files.forEach( (item, index) => {
            console.log( "item",index, "size ", item.contents.length);
        });
    })
    .catch( error => {
        console.log( error );
    });
});

How it Works

这个怎么运作

The promiseAlldoes the magic. It takes a function block of signature function(item,index,resolve,reject), where itemis the current item in the array, indexits position in the array, and resolveand rejectthe Promisecallback functions. Each promise will be pushed in a array at the current indexand with the current itemas arguments through a anonymous function call:

promiseAll做的魔力。这需要签名的功能块function(item,index,resolve,reject),其中item为阵列中的当前项,index其在阵列中的位置,以及resolvereject所述Promise回调函数。每个 Promise 都将被推入当前的数组中,indexitem通过匿名函数调用将当前作为参数:

promises.push( function(item,i) {
        return new Promise(function(resolve, reject) {
            return block.apply(this,[item,index,resolve,reject]);
        });
    }(item,index))

Then all promises will be resolved:

然后所有的承诺都将得到解决:

return Promise.all(promises);

回答by pldg

For all example below you need to import fsand pathmodules:

对于下面的所有示例,您需要导入fspath模块:

const fs = require('fs');
const path = require('path');

Read files asynchronously

异步读取文件

function readFiles(dir, processFile) {
  // read directory
  fs.readdir(dir, (error, fileNames) => {
    if (error) throw error;

    fileNames.forEach(filename => {
      // get current file name
      const name = path.parse(filename).name;
      // get current file extension
      const ext = path.parse(filename).ext;
      // get current file path
      const filepath = path.resolve(dir, filename);

      // get information about the file
      fs.stat(filepath, function(error, stat) {
        if (error) throw error;

        // check if the current path is a file or a folder
        const isFile = stat.isFile();

        // exclude folders
        if (isFile) {
          // callback, do something with the file
          processFile(filepath, name, ext, stat);
        }
      });
    });
  });
}

Usage:

用法:

// use an absolute path to the folder where files are located
readFiles('absolute/path/to/directory/', (filepath, name, ext, stat) => {
  console.log('file path:', filepath);
  console.log('file name:', name);
  console.log('file extension:', ext);
  console.log('file information:', stat);
});

Read files synchronously, store in array, natural sorting

同步读取文件,数组存储,自然排序

/**
 * @description Read files synchronously from a folder, with natural sorting
 * @param {String} dir Absolute path to directory
 * @returns {Object[]} List of object, each object represent a file
 * structured like so: `{ filepath, name, ext, stat }`
 */
function readFilesSync(dir) {
  const files = [];

  fs.readdirSync(dir).forEach(filename => {
    const name = path.parse(filename).name;
    const ext = path.parse(filename).ext;
    const filepath = path.resolve(dir, filename);
    const stat = fs.statSync(filepath);
    const isFile = stat.isFile();

    if (isFile) files.push({ filepath, name, ext, stat });
  });

  files.sort((a, b) => {
    // natural sort alphanumeric strings
    // https://stackoverflow.com/a/38641281
    return a.name.localeCompare(b.name, undefined, { numeric: true, sensitivity: 'base' });
  });

  return files;
}

Usage:

用法:

// return an array list of objects
// each object represent a file
const files = readFilesSync('absolute/path/to/directory/');

Read files async using promise

使用 promise 异步读取文件

More info on promisifyin this article.

这篇文章中有更多关于promisify 的信息。

const { promisify } = require('util');

const readdir_promise = promisify(fs.readdir);
const stat_promise = promisify(fs.stat);

function readFilesAsync(dir) {
  return readdir_promise(dir, { encoding: 'utf8' })
    .then(filenames => {
      const files = getFiles(dir, filenames);

      return Promise.all(files);
    })
    .catch(err => console.error(err));
}

function getFiles(dir, filenames) {
  return filenames.map(filename => {
    const name = path.parse(filename).name;
    const ext = path.parse(filename).ext;
    const filepath = path.resolve(dir, filename);

    return stat({ name, ext, filepath });
  });
}

function stat({ name, ext, filepath }) {
  return stat_promise(filepath)
    .then(stat => {
      const isFile = stat.isFile();

      if (isFile) return { name, ext, filepath, stat };
    })
    .catch(err => console.error(err));
}

Usage:

用法:

readFiles('absolute/path/to/directory/')
  // return an array list of objects
  // each object is a file
  // with those properties: { name, ext, filepath, stat }
  .then(files => console.log(files))
  .catch(err => console.log(err));

Note:return undefinedfor folders, if you want you can filter them out:

注意:返回undefined文件夹,如果需要,可以将它们过滤掉:

readFiles('absolute/path/to/directory/')
  .then(files => files.filter(file => file !== undefined))
  .catch(err => console.log(err));

回答by Bimal Grg

Are you a lazy person like me and love npm module:D then check this out.

你是不是像我一样懒惰并且喜欢npm 模块:D 然后看看这个。

npm install node-dir

npm 安装节点目录

example for reading files:

读取文件的示例:

var dir = require('node-dir');

dir.readFiles(__dirname,
    function(err, content, next) {
        if (err) throw err;
        console.log('content:', content);  // get content of files
        next();
    },
    function(err, files){
        if (err) throw err;
        console.log('finished reading files:', files); // get filepath 
   });    

回答by Marcus

If you have Node.js 8 or later, you can use the new util.promisify. (I'm marking as optional the parts of the code that have to do with reformatting as an object, which the original post requested.)

如果您有 Node.js 8 或更高版本,则可以使用新的 util.promisify。(我将与重新格式化为对象有关的代码部分标记为可选,这是原始帖子要求的。)

  const fs = require('fs');
  const { promisify } = require('util');

  let files; // optional
  promisify(fs.readdir)(directory).then((filenames) => {
    files = filenames; // optional
    return Promise.all(filenames.map((filename) => {
      return promisify(fs.readFile)(directory + filename, {encoding: 'utf8'});
    }));
  }).then((strArr) => {
    // optional:
    const data = {};
    strArr.forEach((str, i) => {
      data[files[i]] = str;
    });
    // send data here
  }).catch((err) => {
    console.log(err);
  });

回答by Paul

Another version with Promise's modern method. It's shorter that the others responses based on Promise :

Promise 的现代方法的另一个版本。其他基于 Promise 的响应更短:

const readFiles = (dirname) => {

  const readDirPr = new Promise( (resolve, reject) => {
    fs.readdir(dirname, 
      (err, filenames) => (err) ? reject(err) : resolve(filenames))
  });

  return readDirPr.then( filenames => Promise.all(filenames.map((filename) => {
      return new Promise ( (resolve, reject) => {
        fs.readFile(dirname + filename, 'utf-8',
          (err, content) => (err) ? reject(err) : resolve(content));
      })
    })).catch( error => Promise.reject(error)))
};

readFiles(sourceFolder)
  .then( allContents => {

    // handle success treatment

  }, error => console.log(error));

回答by rsa

For the code to work smoothy in different enviroments, path.resolvecan be used in places where path is manipulated. Here is code which works better.

为了让代码在不同环境下流畅运行path.resolve可以用于路径被操作的地方。这是效果更好的代码。

Reading part:

阅读部分:

var fs = require('fs');

function readFiles(dirname, onFileContent, onError) {
  fs.readdir(dirname, function(err, filenames) {
    if (err) {
      onError(err);
      return;
    }
    filenames.forEach(function(filename) {
      fs.readFile(path.resolve(dirname, filename), 'utf-8', function(err, content) {
        if (err) {
          onError(err);
          return;
        }
        onFileContent(filename, content);
      });
    });
  });
}

Storing part:

存储部分:

var data = {};
readFiles(path.resolve(__dirname, 'dirname/'), function(filename, content) {
  data[filename] = content;
}, function(error) {
  throw err;
});

回答by Isaac Pak

async/await

异步/等待

const { promisify } = require("util")
const directory = path.join(__dirname, "/tmpl")
const pathnames = promisify(fs.readdir)(directory)

try {
  async function emitData(directory) {
    let filenames = await pathnames
    var ob = {}
    const data = filenames.map(async function(filename, i) {
      if (filename.includes(".")) {
        var storedFile = promisify(fs.readFile)(directory + `\${filename}`, {
          encoding: "utf8",
        })
        ob[filename.replace(".js", "")] = await storedFile
        socket.emit("init", { data: ob })
      }
      return ob
    })
  }

  emitData(directory)
} catch (err) {
  console.log(err)
}

Who wants to try with generators?

谁想尝试发电机?