Узел FTP: несколько асинхронных вызовов внутри цикла

Я знаю, что это какая-то асинхронная головоломка, в которую я попал, но я пытаюсь загрузить несколько файлов внутри обратного вызова из .list метод любого из различных пакетов FTP npm.

Так что-то вроде этого (примечание: JSFTP здесь просто обертка вокруг node-ftpЯ получаю ту же проблему с этим):

  ftp = new JSFTP(conf.FTPConfig)
  ftp.ls(conf.remoteFolder, (err, fileList) => {
    if (err) return console.error(err)

    for (let i=0; i<fileList.length; i++){
      file = fileList[i]
      ftp.get(file.name, `./Downloaded/${file.name}`, err => {
        if (err) return console.error(err)
        console.log(`${file.name} copied.`)
      })
    }
  })

Если я не использую цикл и загружаю только один файл, все работает нормально. Но с циклом я продолжаю получать следующие ошибки, и я не получаю ни один из файлов (кроме одного или двух пустых заполнителей файлов):

(Короче): Error: 503 Bad sequence of commandsи далее ниже: 'Probably trying a PASV operation while one is in progress

Похоже, это сбивает все мои .get вызовы внутри цикла (очевидно, не дожидаясь завершения какого-либо из них, что нормально), но затем не может фактически выполнить все вызовы.get таким образом? Разве они не должны просто работать параллельно, асинхронно?

{ Error: 503 Bad sequence of commands.
    at Ftp.parse (C:\Git\SenecaFTP\FTPMonitor\node_modules\jsftp\lib\jsftp.js:257:11)
    at Ftp.parseResponse (C:\Git\SenecaFTP\FTPMonitor\node_modules\jsftp\lib\jsftp.js:174:8)
    at Stream.<anonymous> (C:\Git\SenecaFTP\FTPMonitor\node_modules\jsftp\lib\jsftp.js:146:24)
    at emitOne (events.js:96:13)
    at Stream.emit (events.js:188:7)
    at ResponseParser.reemit (C:\Git\SenecaFTP\FTPMonitor\node_modules\duplexer\index.js:70:25)
    at emitOne (events.js:96:13)
    at ResponseParser.emit (events.js:188:7)
    at readableAddChunk (_stream_readable.js:172:18)
    at ResponseParser.Readable.push (_stream_readable.js:130:10) code: 503 }
{ Error: 503 Bad sequence of commands.
    at Ftp.parse (C:\Git\SenecaFTP\FTPMonitor\node_modules\jsftp\lib\jsftp.js:257:11)
    at Ftp.parseResponse (C:\Git\SenecaFTP\FTPMonitor\node_modules\jsftp\lib\jsftp.js:174:8)
    at Stream.<anonymous> (C:\Git\SenecaFTP\FTPMonitor\node_modules\jsftp\lib\jsftp.js:146:24)
    at emitOne (events.js:96:13)
    at Stream.emit (events.js:188:7)
    at ResponseParser.reemit (C:\Git\SenecaFTP\FTPMonitor\node_modules\duplexer\index.js:70:25)
    at emitOne (events.js:96:13)
    at ResponseParser.emit (events.js:188:7)
    at readableAddChunk (_stream_readable.js:172:18)
    at ResponseParser.Readable.push (_stream_readable.js:130:10) code: 503 }
{ Error: 503 Bad sequence of commands.
    at Ftp.parse (C:\Git\SenecaFTP\FTPMonitor\node_modules\jsftp\lib\jsftp.js:257:11)
    at Ftp.parseResponse (C:\Git\SenecaFTP\FTPMonitor\node_modules\jsftp\lib\jsftp.js:174:8)
    at Stream.<anonymous> (C:\Git\SenecaFTP\FTPMonitor\node_modules\jsftp\lib\jsftp.js:146:24)
    at emitOne (events.js:96:13)
    at Stream.emit (events.js:188:7)
    at ResponseParser.reemit (C:\Git\SenecaFTP\FTPMonitor\node_modules\duplexer\index.js:70:25)
    at emitOne (events.js:96:13)
    at ResponseParser.emit (events.js:188:7)
    at readableAddChunk (_stream_readable.js:172:18)
    at ResponseParser.Readable.push (_stream_readable.js:130:10) code: 503 }
{ Error: 503 Bad sequence of commands.
    at Ftp.parse (C:\Git\SenecaFTP\FTPMonitor\node_modules\jsftp\lib\jsftp.js:257:11)
    at Ftp.parseResponse (C:\Git\SenecaFTP\FTPMonitor\node_modules\jsftp\lib\jsftp.js:174:8)
    at Stream.<anonymous> (C:\Git\SenecaFTP\FTPMonitor\node_modules\jsftp\lib\jsftp.js:146:24)
    at emitOne (events.js:96:13)
    at Stream.emit (events.js:188:7)
    at ResponseParser.reemit (C:\Git\SenecaFTP\FTPMonitor\node_modules\duplexer\index.js:70:25)
    at emitOne (events.js:96:13)
    at ResponseParser.emit (events.js:188:7)
    at readableAddChunk (_stream_readable.js:172:18)
    at ResponseParser.Readable.push (_stream_readable.js:130:10) code: 503 }
{ Error: 503 Bad sequence of commands.
    at Ftp.parse (C:\Git\SenecaFTP\FTPMonitor\node_modules\jsftp\lib\jsftp.js:257:11)
    at Ftp.parseResponse (C:\Git\SenecaFTP\FTPMonitor\node_modules\jsftp\lib\jsftp.js:174:8)
    at Stream.<anonymous> (C:\Git\SenecaFTP\FTPMonitor\node_modules\jsftp\lib\jsftp.js:146:24)
    at emitOne (events.js:96:13)
    at Stream.emit (events.js:188:7)
    at ResponseParser.reemit (C:\Git\SenecaFTP\FTPMonitor\node_modules\duplexer\index.js:70:25)
    at emitOne (events.js:96:13)
    at ResponseParser.emit (events.js:188:7)
    at readableAddChunk (_stream_readable.js:172:18)
    at ResponseParser.Readable.push (_stream_readable.js:130:10) code: 503 }
{ Error: 503 Bad sequence of commands.
    at Ftp.parse (C:\Git\SenecaFTP\FTPMonitor\node_modules\jsftp\lib\jsftp.js:257:11)
    at Ftp.parseResponse (C:\Git\SenecaFTP\FTPMonitor\node_modules\jsftp\lib\jsftp.js:174:8)
    at Stream.<anonymous> (C:\Git\SenecaFTP\FTPMonitor\node_modules\jsftp\lib\jsftp.js:146:24)
    at emitOne (events.js:96:13)
    at Stream.emit (events.js:188:7)
    at ResponseParser.reemit (C:\Git\SenecaFTP\FTPMonitor\node_modules\duplexer\index.js:70:25)
    at emitOne (events.js:96:13)
    at ResponseParser.emit (events.js:188:7)
    at readableAddChunk (_stream_readable.js:172:18)
    at ResponseParser.Readable.push (_stream_readable.js:130:10) code: 503 }
{ Error: connect ECONNREFUSED 192.168.100.161:61229
    at Object.exports._errnoException (util.js:1007:11)
    at exports._exceptionWithHostPort (util.js:1030:20)
    at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1080:14)
  code: 'ECONNREFUSED',
  errno: 'ECONNREFUSED',
  syscall: 'connect',
  address: '192.168.100.161',
  port: 61229,
  msg: 'Probably trying a PASV operation while one is in progress' }

1 ответ

Решение

После просмотра источника для jsftp выясняется, что он создает одно соединение и использует только один сокет: https://github.com/sergi/jsftp/blob/master/lib/jsftp.js#L120

Это означает, что он не создает новое соединение для каждой команды, но использует одно соединение для отправки команд. В основном, как обойти это, создав новый экземпляр SFTP для каждого файла, который вы хотите загрузить.

В основном переделываем код для этого:

ftp = new JSFTP(conf.FTPConfig)
  ftp.ls(conf.remoteFolder, (err, fileList) => {
    if (err) return console.error(err)

    for (let i=0; i<fileList.length; i++){
      file = fileList[i]
      new JSFTP(conf.FTPConfig).get(file.name, `./Downloaded/${file.name}`, err => {
        if (err) return console.error(err)
        console.log(`${file.name} copied.`)
      })
    }
  })

Однако я бы порекомендовал использовать какую-то библиотеку управления потоком, такую ​​как async или используя решение, основанное на обещаниях, так как оно может быть немного более управляемым.

Существует также аналогичный вопрос о SO с аналогичным решением с использованием async Модуль, о котором я упоминал: загрузка нескольких файлов с ftp сайта с использованием узла js

Другие вопросы по тегам