web-dev-qa-db-ja.com

Node.jsファイル名としてコンテンツの配置を使用してファイルをダウンロードする

Requestモジュールを使用してファイルをダウンロードしていますが、「Content-Disposition」ヘッダーからファイル名を取得する必要がある場合に、出力ストリームに応答をパイプする方法がわかりません。基本的に、ヘッダーが見つかるまで応答を読み取り、残りをそのファイル名にパイプする必要があります。

例は次のようなものを示します。

request('http://google.com/doodle.png').pipe(fs.createWriteStream('doodle.png'));

私がしたい場所(擬似コード):

var req = request('http://example.com/download_latest_version?token=XXX');
var filename = req.response.headers['Content-Disposition'];

req.pipe(fs.createWriteStream(filename));

Requestコールバックを使用してファイル名を取得できます。

request(url, function(err, res, body) {
 // get res headers here
});

しかし、それはパイプを使用し、ダウンロードしたファイルをメモリにロードしないことの利点を無効にしないでしょうか?

24
user3019326

Yahooから画像を取得し、content-dispositionヘッダーを使用していませんが、dateおよびcontent-typeヘッダーを抽出してファイル名を作成しています。これはあなたがやろうとしていることに十分に近いようです...

var request = require('request'),
fs = require('fs');

var url2 = 'http://l4.yimg.com/nn/fp/rsz/112113/images/smush/aaroncarter_635x250_1385060042.jpg';

var r = request(url2);

r.on('response',  function (res) {
  res.pipe(fs.createWriteStream('./' + res.headers.date + '.' + res.headers['content-type'].split('/')[1]));

});

画像の選択を無視してください:)

31
kberg

質問はしばらくの間ありましたが、今日私は同じ問題に直面し、異なって解決しました:

var Request = require( 'request' ),
    Fs = require( 'fs' );

// RegExp to extract the filename from Content-Disposition
var regexp = /filename=\"(.*)\"/gi;

// initiate the download
var req = Request.get( 'url.to/somewhere' )
                 .on( 'response', function( res ){

                    // extract filename
                    var filename = regexp.exec( res.headers['content-disposition'] )[1];

                    // create file write stream
                    var fws = Fs.createWriteStream( '/some/path/' + filename );

                    // setup piping
                    res.pipe( fws );

                    res.on( 'end', function(){
                      // go on with processing
                    });
                 });
13
Sirko

私のソリューションは次のとおりです。

var fs = require('fs');
var request = require('request');
var through2 = require('through2');

var req = request(url);
req.on('error', function (e) {
    // Handle connection errors
    console.log(e);
});
var bufferedResponse = req.pipe(through2(function (chunk, enc, callback) {
    this.Push(chunk);
    callback()
}));
req.on('response', function (res) {
    if (res.statusCode === 200) {
        try {
            var contentDisposition = res.headers['content-disposition'];
            var match = contentDisposition && contentDisposition.match(/(filename=|filename\*='')(.*)$/);
            var filename = match && match[2] || 'default-filename.out';
            var dest = fs.createWriteStream(filename);
            dest.on('error', function (e) {
                // Handle write errors
                console.log(e);
            });
            dest.on('finish', function () {
                // The file has been downloaded
                console.log('Downloaded ' + filename);
            });
            bufferedResponse.pipe(dest);
        } catch (e) {
            // Handle request errors
            console.log(e);
        }
    }
    else {
        // Handle HTTP server errors
        console.log(res.statusCode);
    }
});

ここに投稿された他のソリューションはres.pipeを使用します。応答ストリームに生の(圧縮された)HTTPデータが含まれているため、コンテンツがgzipエンコーディングを使用して転送されると失敗します。この問題を回避するには、代わりにrequest.pipeを使用する必要があります。 ( https://github.com/request/request#examples の2番目の例を参照してください。)

request.pipeを使用するとき、実際にパイピング(ダウンロードしたファイルを保持するディレクトリを作成)する前に非同期処理を行っていたため、「応答からデータが発行された後、パイプできません。」また、ファイルがコンテンツなしで書き込まれている場合、いくつかの問題がありました。これは、requestがHTTP応答を読み取ってバッファリングしたためである可能性があります。

そのため、through2を使用して中間バッファリングストリームを作成し、応答ハンドラーが起動する前にリクエストをパイプ処理し、その後、ファイル名がわかったらバッファリングストリームからファイルストリームにパイプ処理できるようにしました。

最後に、filename*=''file.txt構文を使用してファイル名がプレーン形式またはUTF-8形式でエンコードされているかどうかに関係なく、コンテンツ処理ヘッダーを解析しています。

これが、私と同じ問題を経験している他の人の助けになることを願っています。

4
chris