【问题标题】:Node FTP doesn't execute `once('close')` callback when downloading file is large下载文件较大时,节点 FTP 不执行 `once('close')` 回调
【发布时间】:2017-09-23 23:01:47
【问题描述】:

我有一种从 FTP 服务器下载文件的方法,它适用于较小的文件,但是当我使用它下载约 5GB 大小的 zip 类型的文件时,它会下载它,但在那之后它什么也不做.当它达到 100% 的下载时,脚本不会继续。如果下载完成后它实际上在后台做某事,我应该等待吗?有文件大小限制吗?

const FTP = require('ftp')

可以在npm上找到

downloadFile: params => {
    return new Promise((resolve, reject) => {
      let ftpClient = new FTP()
      let total = params.state.fileSize
      let progress = 0
      ftpClient.on('ready', _ => {
        console.log(`Downloading ${params.targetedFile} ...`);
        ftpClient.get(params.targetedFile, (err, stream) => {
          if (err) reject(err)
          stream.on('data', buffer => {
            progress += buffer.length
            process.stdout.write(`Progress: ${(progress/total*100).toFixed(2)}% (${progress}/${total})  \r`)
          })
          stream.once('close', _ => {
            ftpClient.end()
            console.log(`Saved downloaded file to ${params.localDir}`);
            resolve(params.localDir)
          })
          stream.pipe(fs.createWriteStream(params.localDir))
        })
      })
      ftpClient.connect(params.auth)
    })
  }

基本上,下载大文件时不会执行stream.once('close', ...) 的回调。它会针对相同类型的较小文件执行。

【问题讨论】:

    标签: javascript node.js ftp-client


    【解决方案1】:

    我建议你处理关闭写流的事件。

    原因很简单:我们从 ftp 的读流和管道中读取到写流,一切正常,当文件成功关闭时。

    所以代码:

    downloadFile: params => {
        return new Promise((resolve, reject) => {
          let ftpClient = new FTP()
          let total = params.state.fileSize
          let progress = 0
          ftpClient.on('ready', _ => {
            console.log(`Downloading ${params.targetedFile} ...`);
            ftpClient.get(params.targetedFile, (err, stream) => {
              if (err) {
                ftpClient.end();
                return reject(err);
              }
    
              stream.on('data', buffer => {
                progress += buffer.length
                process.stdout.write(`Progress: ${(progress/total*100).toFixed(2)}% (${progress}/${total})  \r`)
              });
    
              // opening writeStream to file
              let finished = false;
              const writeStream = fs.createWriteStream(params.localDir);
    
              writeStream.on('finish', (result) => { // handling finish
                finished = true;
                ftpClient.end();
                console.log(`Finish triggered ${params.localDir}`);
                console.log(result);
                resolve(params.localDir);
              });
    
              writeStream.on('close', (result) => { // handling close
                ftpClient.end();
                console.log(`Close triggered ${params.localDir}`);
                console.log(result);
                resolve(params.localDir);
              })
    
              // piping readStream to writeStream
              stream.pipe(writeStream);
            })
          })
          ftpClient.connect(params.auth)
        })
      }
    

    【讨论】:

    • 这段代码与我发布的代码有何不同,除了您将fs.createWriteStream(params.localDir) 分配给 const writeStream
    • 更新了我的答案,您应该阅读有关读写流和nodejs stream end vs close
    • 哦,我现在看到了不同。我修改了代码,在小文件上成功运行。现在我要在大文件上测试它,我会回复结果。
    • 我在大文件上运行它。问题仍然存在。它仍然停留在同一点。我的最后一个日志是Progress: 100.00% (4618223308/4618223308) writeStream.once('close', ...) 中的预期日志没有执行,脚本也没​​有继续前进。
    • on('close'(不是once)呢?
    【解决方案2】:

    这段代码可能会让你知道如何以一种有点老套的方式处理这个问题。

    基本上这种方法允许您从 FTP 服务器下载文件并将其保存到本地文件系统。它在一行中输出当前进度complete_percentage% (current/total)。完成后,它解析承诺,返回本地文件的路径,与您作为参数传递的路径相同。

    /**
       * @name downloadFile
       * @desc downloads file from FTP server
       * @param  params, Object of params
       *   @prop auth: object, or null, authorization params
       *   @prop targetedFile: {String} filename e.g. data.txt
       *   @prop localDir: {String} filename on local disk
       *   @prop state: {Object} fileinfo object, {Int} .fileSize property is required
       * @return Promise, resolves given localDir
       */
      downloadFile: params => {
        return new Promise((resolve, reject) => {
          // validate param types
          if(typeof params.auth !== 'object'
          || typeof params.targetedFile !== 'string'
          || typeof params.localDir !== 'string'
          || typeof params.state !== 'object'
          || typeof params.state.fileSize !== 'number'
          ) throw new Error('You are either missing properties or passed wrong types')
    
          // initialize
          let ftpClient = new FTP()
          let total = params.state.fileSize
          let progress = 0
    
          //
          ftpClient.on('ready', _ => {
            console.log(`Downloading ${params.targetedFile} ...`)
            // get file
            ftpClient.get(params.targetedFile, (err, stream) => {
              if (err){
                ftpClient.end()
                return reject(err)
              }
    
              // upon data receive
              stream.on('data', buffer => {
                progress += buffer.length
                // if progress is complete
                if(progress === total){
                  // start checking if local filesize matches server filesize
                  let interval = setInterval(_ => {
                    if(fs.statSync(params.localDir).size === total){
                      console.log(`Downloading file complete. Location: ${params.localDir}`);
                      clearInterval(interval)
                      ftpClient.end()
                      resolve(params.localDir)
                    }
                  })
                }
                // show current progress in percentages and bytes
                process.stdout.write(`Progress: ${(progress/total*100).toFixed(2)}% (${progress}/${total})  \r`)
              })
              // pipe writestream to filesystem to write these bytes
              stream.pipe(fs.createWriteStream(params.localDir))
            })
          })
          ftpClient.connect(params.auth)
        })//promise
      }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-26
      • 1970-01-01
      • 2017-07-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多