2016-09-29 4 views
0

私は小さなnodejsスクリプトを持っていると私は、zipファイルをダウンロードします:リクエストとnodejを使用してレスポンスヘッダに基づいてリクエストを再試行する方法は?

const fs = requrie("fs"); 
const request = require("requestretry"); 

export class FileFetcher { 
    public async fetchFile(url: string): Promise<string> { 
     const fileName = "./download.zip"; 
     return new Promise<string>(resolve => { 
      request(url) 
       .pipe(fs.createWriteStream(fileName)) 
       .on("close", function() { 
        resolve(fileName); 
       }); 
     }); 
    } 
} 

ファイルのみが、将来的に存在しているので、意志最も可能性が高いこととして、私は、requestのラッパーをrequestretryライブラリを使用しています最初のカップル時間失敗します。

しかし、私は変な振る舞いをする外部エンドポイントに適応する必要があります。期待される404 Not Foundの代わりに常に200 OKを返します。

したがって、応答を確認する唯一の方法は、Content-Lengthヘッダーです。 Content-Length: 0の場合、ファイルはありません。

requestretryでは、異なる再試行戦略があるようですが、それらはすべて、幸福なapiを想定しているようです。 request.RetryStrategies.HTTPOrNetworkError。 200になると、再試行は行われず、空のzipファイルが "正常に"ダウンロードされます。

requestretryを使用して固定されていないため、レスポンスヘッダーに基づいてリクエストを再試行する方法が不思議です。

答えて

0

あなたは例えば、retryStrategyを使用して独自の再試行ロジックを定義することができます。:

request({ 
    url: '...', 
    retryStrategy: function(err, res, body) { 
    return res.headers['Content-Length'] === 0; 
    } 
}); 
0

を私はrequestretryライブラリを捨ててしまったとrequestを行って、再試行メカニズムを自分で実装しました。

1つの問題は、実際のレスポンスボディデータを取得することでした。私はそれがresponseイベントを通じて利用可能であることを期待していましたが、これにはヘッダーしかありません。

データは、複数のデータイベントによってフェッチされ、終了イベントで約束を解決することができます。

もう1つの問題は、encodingがnullである必要があるか、ダウンロードしたファイルが文字列にキャストされて破損するためです。バッファはfs.writeFile(file, buffer)を経由して書き込むことができ

export class FileFetcher { 
    public async downloadWithRetry(url: string, maxRetries: number, timeout: number): Promise<Buffer> { 
     while (true) { 
      try { 
       const buffer = await this.fetchFile(url); 
       return new Promise<Buffer>(resolve => { 
        resolve(buffer); 
       }); 
      } catch (e) { 
       maxRetries = maxRetries - 1; 

       if (maxRetries <= 0) { 
        throw new Error("Too many requests"); 
       } 

       console.warn(`No file at url:${url}, waiting ...`); 
       await this.wait(timeout); 
       console.log(`Continue.`); 
      } 
     } 
    } 

    private async wait(timeout: number): Promise<any> { 
     timeout *= 1e3; 
     console.log(timeout); 
     return new Promise(resolve => { 
      setTimeout(resolve, timeout); 
     }); 
    } 

    private async fetchFile(url: string): Promise <Buffer> { 
     return new Promise<Buffer>((resolve, reject) => { 
      let data = []; 
      request.get({ 
       encoding: null, 
       url: url, 
      }).on("data", function (chunk) { 
       data.push(chunk); 
      }).on("response", function (response) { 
       /** 
       * Server always returns 200 OK even if file does not exist yet. Hence checking for content-lenth head 
       */ 
       if (!(response.headers["content-length"] > 0)) { 
        reject("Empty response!"); 
       } 
      }).on("end", function() { 
       const body = Buffer.concat(data); 
       if (body.length > 0) { 
        resolve(body); 
        return; 
       } 

       reject("Empty response"); 
      }); 
     }); 
    } 
} 

マイダウンローダクラスは次のようになります。

データをパイプしたかったので、下側がストリームアプローチを使用していません。しかし、この方法では少なくともファイルを適切に取得します。

関連する問題