Javascript fs.copy无法处理大量图像,只能处理少量图像

Javascript fs.copy无法处理大量图像,只能处理少量图像,javascript,node.js,file,asynchronous,Javascript,Node.js,File,Asynchronous,我有一个文件名数组,我正试图从一个目录复制到另一个目录。文件名在async系列链中的函数中构造,然后在最后一个函数中,使用fs.copy复制文件名。脚本也在十个不同的目录上运行,所以我看到的是这样的。它被简化了,但功能是相同的 var dirs = [{ 'src': 'dir1', 'dest': 'dest/dir1', 'files': [] }, { 'src': 'dir2', 'dest': 'dest/dir2', 'files':

我有一个文件名数组,我正试图从一个目录复制到另一个目录。文件名在
async
系列
链中的函数中构造,然后在最后一个函数中,使用
fs.copy
复制文件名。脚本也在十个不同的目录上运行,所以我看到的是这样的。它被简化了,但功能是相同的

var dirs = [{
    'src': 'dir1',
    'dest': 'dest/dir1',
    'files': []
}, {
    'src': 'dir2',
    'dest': 'dest/dir2',
    'files': []
}, {
    'src': 'dir3',
    'dest': 'dest/dir3',
    'files': []
}, {
    'src': 'dir4',
    'dest': 'dest/dir4',
    'files': []
}];

async.series([function(callback){
 //get files
 dirs.forEach(function(currentSrc){
    fs.readdirSync(currentSrc);
 });
 callback();
},
function(callback){
  //make dest dirs with dirs.forEach and fs.mkdir
  callback();
},
function(callback){
  var src
    , dest;
  dirs.forEach(funtion(dir){
    dir.files.forEach(function(file){
      src = path.join(dir.src, file);
      dest = path.join(dir.dest, file);
      fs.copy(src, dest, {replace: false}, function(err) {
        if (err){
          console.log('error copying file: ', err);
        }
    });
  });
}]);
这对少量文件很好,但是当我尝试使用包含大约400MB的目录时,失败了。所有文件似乎都在目标中,但除了名称(正确的名称)之外还有do数据,但每个文件的文件大小都是0。为什么这适用于少量文件,但不适用于大型文件

更新。我得到了错误

events.js:85 投掷者;//未处理的“错误”事件

更新: 我现在使用@Jacob提供的策略,我得到的是:

var dirs = [{
    'src': 'src/1',
    'dest': 'waterfallDest1',
    'files': []
}, {
    'src': 'src/2',
    'dest': 'waterfallDest2',
    'files': []
}, {
    'src': 'src/3',
    'dest': 'waterfallDest3',
    'files': []
}, {
    'src': 'src/4',
    'dest': 'waterfallDest4',
    'files': []
}];




async.eachLimit(dirs, 1000, function (dir, cb) {
    async.waterfall([
        function (cb) {
            fs.mkdir(dir.dest, cb);
        },
        function (cb) {
            fs.readdir(dir.src, cb);
        },
        function (files, cb) {
            async.eachLimit(files, 10, function (file, cb) {
                var src = path.join(dir.src, file);
                var dest = path.join(dir.dest, file);
                try { // In case fs.copy is indeed throwing an error
                    fs.copy(src, dest, {replace: false}, cb);
                } catch (err) {
                    cb('try-catch err ', err);
                }
            }, cb);
        }
    ], cb);
}, function (err) {
    if (err) {
        console.log('Some error happened:\n' + err.stack);
    }
});

这将成功创建所有目录,并成功地将文件传输到第一个目录中,但随后的每个目录中都充满了0k映像。

您需要为
async.series
调用进行最终回调:

async.series([
  function (callback) {
    dirs.forEach(function (currentSrc) {
      fs.readdirSync(currentSrc);
    });
    callback();
  },
  function (callback) {
    //make dest dirs with dirs.forEach and fs.mkdir
    callback();
  },
  function(callback) {
    var src, dest;
    dirs.forEach(funtion(dir){
      dir.files.forEach(function(file){
        src = path.join(dir.src, file);
        dest = path.join(dir.dest, file);
        fs.copy(src, dest, {replace: false}, function(err) {
          if (err){
            console.log('error copying file: ', err);
            // Did you want to do callback(err)?
          }
        });
      });
    })
    // Somewhere, this needs to call back.
  }
], function (err, result) {
  if (err) {
    // One of the steps had an error; handle it.
  }
});
下面是代码如何使用
async.each
来避免所有这些循环。它还包括避免操作系统一次打开过多文件的限制:

async.eachLimit(dirs, 10, function (dir, cb) {
  async.waterfall([
    function (cb) {
      fs.mkdir(dir.dest, cb);
    },
    function (cb) {
      fs.readdir(dir.src, cb);
    },
    function (files, cb) {
      async.eachLimit(files, 10, function (file, cb) {
        var src = path.join(dir.src, file);
        var dest = path.join(dir.dest, file);
        try { // In case fs.copy is indeed throwing an error
          fs.copy(src, dest, {replace: false}, cb);
        } catch (err) {
          cb(err);
        }
      }, cb);
    }
  ], cb);
}, function (err) {
  if (err) {
    console.log('Some error happened:\n' + err.stack);
  }
});
Per“您可以使用try/catch处理异常或允许异常冒泡。”


无论如何,底层库ncp应该为fs中的零星回调负责如果ncp幕后对单个文件多次调用回调,则第二次回调将引发异常,该异常。。。该文件已经存在。

我很好奇,既然您已经在使用
async
,为什么要使用
readdirSync
。@Jacob这可能是我之前使用过的东西的残余。我对这一切都很陌生。你建议我怎么做?可能1000的限制太多了,因为它是1000x10(并发文件)=10000个文件被并行复制。谢谢。“在某个地方需要回拨。”因为同步中的forEach
forEach
,可能这就是问题所在。也许我可以用
async.each
?如果是这样的话,回调会去哪里?我注意到您只是将
cb
s作为参数传递给各种函数,但从不调用它们。这是故意的吗?在
async.each
的情况下,
async.failter
将调用回调。类似地,其他回调传递给异步函数,异步函数将负责调用它们。你只需要在其他事情还没有做的时候调用回调。我已经实现了你提供给我的瀑布式解决方案。谢谢但是,只有文件成功传输到第一个目录。在那之后,后续文件似乎在各自的
dest
dir中,但只是名称。都是0公里。我已经用我目前掌握的信息更新了我的问题。您认为您可以再看一看吗?另外,这种方法是否不显式调用回调标准?在异步API上,在每个函数的末尾显式调用回调。谢谢。这个错误意味着什么?文件根本不存在?为什么不在
cp
回调中处理呢?嗨。
try-catch
块未产生任何错误。获取
ENFILE时出错,请打开
,然后输入其中一个文件的名称。你知道那可能是什么吗?是否认为打开文件是
cp
的一部分,而这是失败的。如果是这样,我的
try-catch
没有捕获到它,这很奇怪。
ENFILE
表示
文件表溢出
;您可能同时打开了太多文件。有关使用
eachLimit
避免太多并发任务的信息,请参阅我的更新。对于异步错误,try-catch不起作用。(即使是这样,简单地捕获错误并忽略它也不是正确的解决方案。)
错误默认情况下,任何事件发射器上的事件都会引发异常-请参阅。如果您有权访问有问题的事件发射器,则可以通过设置
错误处理程序来捕获它。但我不明白你怎么能做到,这在我看来似乎是
fs
的一个缺点。Jacob的解决方案(
async.eachLimit
)可能是下一个最好的解决方案。@Thomas哦,很好,您现在终于在ENFILE中遇到了一个有用的错误。正如Jacob所建议的,有一个打开的文件限制,您已经超过了它。节点的一个优点是它可以很好地扩展。节点的一个缺点是它可以触发多个可以达到这样极限的东西。
  try {
    fs.copy(src, dest, {replace: false});
  } catch(e) {
    if ( e.code != 'EEXIST' ) throw e; // I'm guessing here that this
                                       // is your problem.
  }