2017-04-26 7 views
5

Mam metodę pobierania pliku z serwera FTP i działa dobrze na mniejszych plikach, ale kiedy używam go do pobrania pliku ~ 5GB rozmiar typu zip, to go pobiera, ale potem nic nie robi. Kiedy osiągnie 100% pobierania, skrypt nie będzie kontynuowany. Czy powinienem poczekać, czy faktycznie robi się coś w tle po zakończeniu pobierania? Czy istnieje limit rozmiaru pliku?Węzeł FTP nie wykonuje komendy `once ('close')`, gdy pobieranie pliku jest duże

const FTP = require('ftp') 

które można znaleźć na 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) 
    }) 
    } 

Zasadniczo zwrotna dla stream.once('close', ...) nie zostanie wykonane, gdy duży plik zostanie pobrany. I zostanie wykonany dla mniejszego pliku tego samego typu.

Odpowiedz

0

Kod ten może dać pojęcia jak obsługiwać to w nieco hacky sposób.

Zasadniczo ta metoda umożliwia pobranie pliku z serwera FTP i zapisanie go w lokalnym systemie plików. Wyświetla bieżący postęp complete_percentage% (current/total) w jednym wierszu. Po zakończeniu, rozwiązuje obietnicę, zwraca ścieżkę do pliku lokalnego, tę samą, którą przekazałeś jako parametr.

/** 
    * @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 
    } 
0

Polecam obsługę zdarzenia zamknięcia strumienia zapisu.

Powód jest prosty: czytamy ze strumienia odczytu ftp i potoku do zapisu strumienia, wszystko jest w porządku, gdy plik zostanie pomyślnie zamknięty.

Więc kod:

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) 
    }) 
    } 
+0

Jak to jest kod inny niż ten, który napisałem z wyjątkiem przypisany 'fs.createWriteStream (params.localDir)' 'writeStream' do const? – Kunok

+0

zaktualizowałeś moją odpowiedź, powinieneś przeczytać o strumieniach odczytu i zapisu oraz 'strumieniu strumienia nodejs vs zamknij' – num8er

+1

Och, widzę różnicę teraz. Zmodyfikowałem kod, uruchomiłem go z powodzeniem na małym pliku. Teraz mam zamiar przetestować go na dużym pliku, odpowiem z wynikiem. – Kunok

Powiązane problemy