Nodejs Express fs 将文件迭代到数组或对象失败

发布于 2024-11-14 08:15:22 字数 495 浏览 1 评论 0原文

因此,我尝试使用 Nodejs Express FS 模块来迭代我的应用程序中的目录,将每个文件名存储在一个数组中,我可以将其传递到我的 Express 视图并迭代列表,但我很难做到这一点。当我在 files.forEach 函数循环中执行 console.log 时,它打印文件名很好,但是一旦我尝试执行以下操作,例如:

var myfiles = [];
var fs = require('fs');
fs.readdir('./myfiles/', function (err, files) { if (err) throw err;
  files.forEach( function (file) {
    myfiles.push(file);
  });
});
console.log(myfiles);

它失败了,就只记录一个空对象。所以我不确定到底发生了什么,我认为这与回调函数有关,但是如果有人可以引导我了解我做错了什么,以及为什么它不起作用(以及如何使其工作),那就是非常感谢。

So Im trying to use the nodejs express FS module to iterate a directory in my app, store each filename in an array, which I can pass to my express view and iterate through the list, but Im struggling to do so. When I do a console.log within the files.forEach function loop, its printing the filename just fine, but as soon as I try to do anything such as:

var myfiles = [];
var fs = require('fs');
fs.readdir('./myfiles/', function (err, files) { if (err) throw err;
  files.forEach( function (file) {
    myfiles.push(file);
  });
});
console.log(myfiles);

it fails, just logs an empty object. So Im not sure exactly what is going on, I think it has to do with callback functions, but if someone could walk me through what Im doing wrong, and why its not working, (and how to make it work), it would be much appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

我要还你自由 2024-11-21 08:15:22

myfiles 数组为空,因为在调用 console.log() 之前尚未调用回调。

您需要执行以下操作:

var fs = require('fs');
fs.readdir('./myfiles/',function(err,files){
    if(err) throw err;
    files.forEach(function(file){
        // do something with each file HERE!
    });
 });
 // because trying to do something with files here won't work because
 // the callback hasn't fired yet.

请记住,节点中的所有内容都会同时发生,从某种意义上说,除非您在回调中进行处理,否则无法保证异步函数已完成。

解决这个问题的一种方法是使用 EventEmitter:

var fs=require('fs'),
    EventEmitter=require('events').EventEmitter,
    filesEE=new EventEmitter(),
    myfiles=[];

// this event will be called when all files have been added to myfiles
filesEE.on('files_ready',function(){
  console.dir(myfiles);
});

// read all files from current directory
fs.readdir('.',function(err,files){
  if(err) throw err;
  files.forEach(function(file){
    myfiles.push(file);
  });
  filesEE.emit('files_ready'); // trigger files_ready event
});

The myfiles array is empty because the callback hasn't been called before you call console.log().

You'll need to do something like:

var fs = require('fs');
fs.readdir('./myfiles/',function(err,files){
    if(err) throw err;
    files.forEach(function(file){
        // do something with each file HERE!
    });
 });
 // because trying to do something with files here won't work because
 // the callback hasn't fired yet.

Remember, everything in node happens at the same time, in the sense that, unless you're doing your processing inside your callbacks, you cannot guarantee asynchronous functions have completed yet.

One way around this problem for you would be to use an EventEmitter:

var fs=require('fs'),
    EventEmitter=require('events').EventEmitter,
    filesEE=new EventEmitter(),
    myfiles=[];

// this event will be called when all files have been added to myfiles
filesEE.on('files_ready',function(){
  console.dir(myfiles);
});

// read all files from current directory
fs.readdir('.',function(err,files){
  if(err) throw err;
  files.forEach(function(file){
    myfiles.push(file);
  });
  filesEE.emit('files_ready'); // trigger files_ready event
});
本王不退位尔等都是臣 2024-11-21 08:15:22

正如一些人提到的,您使用的是异步方法,因此您有一个不确定的执行路径。

然而,有一个简单的方法可以解决这个问题。只需使用该方法的同步版本:

var myfiles = [];
var fs = require('fs');

var arrayOfFiles = fs.readdirSync('./myfiles/');

//Yes, the following is not super-smart, but you might want to process the files. This is how:
arrayOfFiles.forEach( function (file) {
    myfiles.push(file);
});
console.log(myfiles);

这应该可以按照您的意愿工作。但是,使用同步语句并不好,因此除非同步非常重要,否则不应该这样做。

在这里阅读更多内容:fs.readdirSync

As several have mentioned, you are using an async method, so you have a nondeterministic execution path.

However, there is an easy way around this. Simply use the Sync version of the method:

var myfiles = [];
var fs = require('fs');

var arrayOfFiles = fs.readdirSync('./myfiles/');

//Yes, the following is not super-smart, but you might want to process the files. This is how:
arrayOfFiles.forEach( function (file) {
    myfiles.push(file);
});
console.log(myfiles);

That should work as you want. However, using sync statements is not good, so you should not do it unless it is vitally important for it to be sync.

Read more here: fs.readdirSync

薄荷梦 2024-11-21 08:15:22

fs.readdir 是异步的(与 Node.js 中的许多操作一样)。这意味着 console.log 行将在 readdir 有机会调用传递给它的函数之前运行。

您需要:

console.log 行放入给 readdir 的回调函数中,即:

fs.readdir('./myfiles/', function (err, files) { if (err) throw err;
  files.forEach( function (file) {
    myfiles.push(file);
  });
  console.log(myfiles);
});

或者简单地对 forEach< 中的每个文件执行一些操作/代码>。

fs.readdir is asynchronous (as with many operations in node.js). This means that the console.log line is going to run before readdir has a chance to call the function passed to it.

You need to either:

Put the console.log line within the callback function given to readdir, i.e:

fs.readdir('./myfiles/', function (err, files) { if (err) throw err;
  files.forEach( function (file) {
    myfiles.push(file);
  });
  console.log(myfiles);
});

Or simply perform some action with each file inside the forEach.

海风掠过北极光 2024-11-21 08:15:22

我认为这与回调函数有关,

确实如此。

fs.readdir 向文件系统发出异步请求以获取该信息,并在稍后的某个时间使用结果调用回调。

因此,function (err, files) { ... } 不会立即运行,但 console.log(myfiles) 会立即运行。

在稍后的某个时间点,myfiles 将包含所需的信息。

顺便说一句,您应该注意 files 已经是一个数组,因此手动将每个元素附加到其他空白数组实际上没有意义。如果想法是将多个调用的结果放在一起,则使用 .concat;如果你只想获取一次数据,那么你可以直接分配myfiles = files

总的来说,您确实应该阅读“Continuation-passing style”

I think it has to do with callback functions,

Exactly.

fs.readdir makes an asynchronous request to the file system for that information, and calls the callback at some later time with the results.

So function (err, files) { ... } doesn't run immediately, but console.log(myfiles) does.

At some later point in time, myfiles will contain the desired information.

You should note BTW that files is already an Array, so there is really no point in manually appending each element to some other blank array. If the idea is to put together the results from several calls, then use .concat; if you just want to get the data once, then you can just assign myfiles = files directly.

Overall, you really ought to read up on "Continuation-passing style".

盛装女皇 2024-11-21 08:15:22

我遇到了同样的问题,根据这篇文章中给出的答案,我用承诺解决了这个问题,这在这种情况下似乎非常有用:

router.get('/', (req, res) => {
  var viewBag = {}; // It's just my little habit from .NET MVC ;)

  var readFiles = new Promise((resolve, reject) => {
    fs.readdir('./myfiles/',(err,files) => {
      if(err) { 
        reject(err); 
      } else {
        resolve(files);
      }
    });
  });

  // showcase just in case you will need to implement more async operations before route will response
  var anotherPromise = new Promise((resolve, reject) => {
    doAsyncStuff((err, anotherResult) => {
      if(err) { 
        reject(err); 
      } else {
        resolve(anotherResult);
      }
    });
  });

  Promise.all([readFiles, anotherPromise]).then((values) => {
    viewBag.files = values[0];
    viewBag.otherStuff = values[1];
    console.log(viewBag.files); // logs e.g. [ 'file.txt' ]
    res.render('your_view', viewBag);
  }).catch((errors) => {
    res.render('your_view',{errors:errors}); // you can use 'errors' property to render errors in view or implement different error handling schema
  });
});

注意:你不必将找到的文件推送到新数组中,因为您已经从 fs.readdir()'c 回调中获取了一个数组。根据节点文档

回调获取两个参数(错误,文件)其中files是一个数组
目录中不包括“.”的文件名和“..”。

我相信这是非常优雅和方便的解决方案,最重要的是 - 它不需要您在脚本中引入和处理新模块。

I faced the same problem, and basing on answers given in this post I've solved it with Promises, that seem to be of perfect use in this situation:

router.get('/', (req, res) => {
  var viewBag = {}; // It's just my little habit from .NET MVC ;)

  var readFiles = new Promise((resolve, reject) => {
    fs.readdir('./myfiles/',(err,files) => {
      if(err) { 
        reject(err); 
      } else {
        resolve(files);
      }
    });
  });

  // showcase just in case you will need to implement more async operations before route will response
  var anotherPromise = new Promise((resolve, reject) => {
    doAsyncStuff((err, anotherResult) => {
      if(err) { 
        reject(err); 
      } else {
        resolve(anotherResult);
      }
    });
  });

  Promise.all([readFiles, anotherPromise]).then((values) => {
    viewBag.files = values[0];
    viewBag.otherStuff = values[1];
    console.log(viewBag.files); // logs e.g. [ 'file.txt' ]
    res.render('your_view', viewBag);
  }).catch((errors) => {
    res.render('your_view',{errors:errors}); // you can use 'errors' property to render errors in view or implement different error handling schema
  });
});

Note: you don't have to push found files into new array because you already get an array from fs.readdir()'c callback. According to node docs:

The callback gets two arguments (err, files) where files is an array
of the names of the files in the directory excluding '.' and '..'.

I belive this is very elegant and handy solution, and most of all - it doesn't require you to bring in and handle new modules to your script.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文