(サードパーティのライブラリを使用せずに)Node.jsでファイルをダウンロードするにはどうすればよいですか?


443

サードパーティのライブラリを使用せずに Node.jsでファイルをダウンロードするにはどうすればよいですか?

特別なものは必要ありません。特定のURLからファイルをダウンロードして、それを特定のディレクトリに保存するだけです。


5
「node.jsでファイルをダウンロードする」 - サーバーにアップロードすることを意味しますか?またはあなたのサーバーを使用してリモートサーバーからファイルを取得しますか?または、node.jsサーバーからダウンロードするファイルをクライアントに提供しますか?
ジョセフ

66
「特定のURLからファイルをダウンロードして、それを特定のディレクトリに保存したいだけです」とは、かなり明確に思えます。:)
ミシェル・ティリー2012

34
ジョセフはすべてのノードプロセスがサーバープロセスであるという誤った主張をしています
lededje

1
@lededjeサーバープロセスがファイルをダウンロードしてサーバー上のディレクトリに保存するのを妨げているものは何ですか。それは完全に実行可能です。
ガーマン

回答:


598

HTTP GETリクエストを作成し、それresponseを書き込み可能なファイルストリームにパイプすることができます。

const http = require('http');
const fs = require('fs');

const file = fs.createWriteStream("file.jpg");
const request = http.get("http://i3.ytimg.com/vi/J---aiyznGQ/mqdefault.jpg", function(response) {
  response.pipe(file);
});

ターゲットファイルやディレクトリ、URLの指定など、コマンドラインでの情報収集をサポートする場合は、Commanderなどを確認してください。


3
このスクリプトを実行すると、次のコンソール出力が表示されましたnode.js:201 throw e; // process.nextTick error, or 'error' event on first tick ^ Error: connect ECONNREFUSED at errnoException (net.js:646:11) at Object.afterConnect [as oncomplete] (net.js:637:18)
アンダーソングリーン

そのhttp.get行で別のURLを使用してみてください。たぶんhttp://i3.ytimg.com/vi/J---aiyznGQ/mqdefault.jpg(そしてに置き換えfile.pngてくださいfile.jpg)。
ミシェルティリー2013年

8
このコードは、スクリプトが終了したときにファイルを適切に閉じますか、それともデータを失いますか?
philk

2
@quantumpotatoリクエストから返された応答を見てみましょう
ミシェルティリー

6
これは、https使用する必要があることを要求している場合はreq urlタイプに依存し、httpsそうでない場合はエラーがスローされます。
Krishnadas PC '19

523

エラーを処理することを忘れないでください!次のコードは、アウグストロマンの答えに基づいています。

var http = require('http');
var fs = require('fs');

var download = function(url, dest, cb) {
  var file = fs.createWriteStream(dest);
  var request = http.get(url, function(response) {
    response.pipe(file);
    file.on('finish', function() {
      file.close(cb);  // close() is async, call cb after close completes.
    });
  }).on('error', function(err) { // Handle errors
    fs.unlink(dest); // Delete the file async. (But we don't check the result)
    if (cb) cb(err.message);
  });
};

2
@ vince-yuan download()自体は可能pipeですか?
rasx

@theGrayFoxこの回答のコードは、承認されたコードよりもはるかに長いためです。:)
pootow 2015

2
@Abdulは、node.js / javascriptが非常に新しいようです。このチュートリアルをご覧ください:tutorialspoint.com/nodejs/nodejs_callbacks_concept.htm複雑ではありません。
Vince Yuan

1
@Abdulたぶん、あなたが理解したことをクラスの残りのメンバーと共有するのは良いことでしょうか?
Curtwagner1984

5
ダウンロードの速度を確認する方法はありますか?何MB /秒を追跡できますか?ありがとう!
Tino Caer 2017

137

ミシェル・ティリーが言ったように、しかし適切な制御フローで:

var http = require('http');
var fs = require('fs');

var download = function(url, dest, cb) {
  var file = fs.createWriteStream(dest);
  http.get(url, function(response) {
    response.pipe(file);
    file.on('finish', function() {
      file.close(cb);
    });
  });
}

finishイベントを待たずに、単純なスクリプトが不完全なファイルになる可能性があります。

編集:明示的に呼び出すのではなく、cbに渡す必要があることを指摘してくれた@Augusto Romanに感謝しfile.closeます。


3
コールバックは私を混乱させます。今を呼び出す場合download()、どうすればよいですか?cb議論として何を置くか?私は持ってdownload('someURI', '/some/destination', cb)いますが、何をcbに入れるかわかりません
Abdul

1
@Abdulファイルが正常にフェッチされたときに何かを行う必要がある場合にのみ、関数でコールバックを指定します。
CatalinBerta 2016年

65

エラーの処理と言えば、リクエストエラーをリッスンすることもできます。応答コードをチェックして検証することもできます。ここでは、200応答コードの場合のみ成功と見なされますが、他のコードが良い場合もあります。

const fs = require('fs');
const http = require('http');

const download = (url, dest, cb) => {
    const file = fs.createWriteStream(dest);

    const request = http.get(url, (response) => {
        // check if response is success
        if (response.statusCode !== 200) {
            return cb('Response status was ' + response.statusCode);
        }

        response.pipe(file);
    });

    // close() is async, call cb after close completes
    file.on('finish', () => file.close(cb));

    // check for request error too
    request.on('error', (err) => {
        fs.unlink(dest);
        return cb(err.message);
    });

    file.on('error', (err) => { // Handle errors
        fs.unlink(dest); // Delete the file async. (But we don't check the result) 
        return cb(err.message);
    });
};

このコードは比較的単純ですが、リクエストモジュールはでネイティブにサポートされていない多くのプロトコル(hello HTTPS!)を処理するため、使用することをお勧めしますhttp

それはそのように行われます:

const fs = require('fs');
const request = require('request');

const download = (url, dest, cb) => {
    const file = fs.createWriteStream(dest);
    const sendReq = request.get(url);

    // verify response code
    sendReq.on('response', (response) => {
        if (response.statusCode !== 200) {
            return cb('Response status was ' + response.statusCode);
        }

        sendReq.pipe(file);
    });

    // close() is async, call cb after close completes
    file.on('finish', () => file.close(cb));

    // check for request errors
    sendReq.on('error', (err) => {
        fs.unlink(dest);
        return cb(err.message);
    });

    file.on('error', (err) => { // Handle errors
        fs.unlink(dest); // Delete the file async. (But we don't check the result)
        return cb(err.message);
    });
};

2
要求モジュールは、HTTPに対して直接機能します。涼しい!
チアゴC. Sベンチュラ2016年

@venturaはい、ところで、安全な接続を処理できるネイティブhttpsモジュールもあります
Buzut

間違いなくエラーが発生しやすくなります。とにかく、リクエストモジュールの使用がオプションである場合は、レベルが非常に高いため、より簡単で効率的であるため、それをお勧めします。
Buzut

2
@アレックス、いや、これはエラーメッセージであり、戻りがあります。したがってresponse.statusCode !== 200、cb on finishが呼び出されることはありません。
Buzut 2017

1
リクエストモジュールを使用した例を示していただきありがとうございます。
ピートアルヴィン

48

gfxmonkの答えには、コールバックとfile.close()完了の間の非常に厳しいデータ競合があります。 file.close()実際には、クローズが完了したときに呼び出されるコールバックを受け取ります。そうしないと、ファイルをすぐに使用できないことがあります(ごくまれに!)。

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

var http = require('http');
var fs = require('fs');

var download = function(url, dest, cb) {
  var file = fs.createWriteStream(dest);
  var request = http.get(url, function(response) {
    response.pipe(file);
    file.on('finish', function() {
      file.close(cb);  // close() is async, call cb after close completes.
    });
  });
}

単純なスクリプトは、終了イベントを待たずに、不完全なファイルになる可能性があります。cbcloseを介してコールバックをスケジュールしないと、ファイルへのアクセスと実際に準備ができているファイルとの間で競合が発生する可能性があります。


2
リクエストを変数に保存する理由は何ですか?
polkovnikov.ph 2014

彼はそれを変数に「格納」するため、デフォルトではグローバル変数にはなりません。
2015

@philk var request =が削除された場合、どのようにしてグローバル変数が作成されたことがわかりますか?
ma11hew28

そうです、リクエストを保存する必要はありません。それはとにかく使用されません。それがあなたの意味ですか?
フィリック

17

node.jsが変更された可能性がありますが、他のソリューション(node v8.1.2を使用)にはいくつか問題があるようです:

  1. あなたは呼び出す必要はありませんfile.close()finishイベント。デフォルトでfs.createWriteStreamは、これはautoCloseに設定されています:https ://nodejs.org/api/fs.html#fs_fs_createwritestream_path_options
  2. file.close()エラー時に呼び出す必要があります。ファイルが削除された場合(unlink())にはこれは必要ないかもしれませんが、通常は次のようになりますhttps : //nodejs.org/api/stream.html#stream_readable_pipe_destination_options
  3. 一時ファイルは削除されません statusCode !== 200
  4. fs.unlink() コールバックなしは非推奨です(警告を出力します)
  5. destファイルが存在する場合; 上書きされます

以下は、これらの問題を処理する修正されたソリューション(ES6とpromiseを使用)です。

const http = require("http");
const fs = require("fs");

function download(url, dest) {
    return new Promise((resolve, reject) => {
        const file = fs.createWriteStream(dest, { flags: "wx" });

        const request = http.get(url, response => {
            if (response.statusCode === 200) {
                response.pipe(file);
            } else {
                file.close();
                fs.unlink(dest, () => {}); // Delete temp file
                reject(`Server responded with ${response.statusCode}: ${response.statusMessage}`);
            }
        });

        request.on("error", err => {
            file.close();
            fs.unlink(dest, () => {}); // Delete temp file
            reject(err.message);
        });

        file.on("finish", () => {
            resolve();
        });

        file.on("error", err => {
            file.close();

            if (err.code === "EEXIST") {
                reject("File already exists");
            } else {
                fs.unlink(dest, () => {}); // Delete temp file
                reject(err.message);
            }
        });
    });
}

1
この上の2件のコメント:1)それはおそらくエラーオブジェクトではなく、文字列を拒否するべきで、必ずしもあなたがやりたいことではないかもしれません2)fs.unlinkます静かにツバメのエラー
リチャードNienaber

1
これはうまくいきます!URLがHTTPSを使用している場合は、代わりconst https = require("https");const http = require("http");
Russ

15

タイムアウト付きの解決策、メモリリークの防止:

次のコードは、Brandon Tilleyの回答に基づいています。

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

var request = http.get("http://example12345.com/yourfile.html", function(response) {
    if (response.statusCode === 200) {
        var file = fs.createWriteStream("copy.html");
        response.pipe(file);
    }
    // Add timeout.
    request.setTimeout(12000, function () {
        request.abort();
    });
});

エラーが発生したときにファイルを作成しないでください。タイムアウトを使用して、X秒後にリクエストを閉じてください。


1
これは単なるファイルで、ダウンロードするプロトコルやサーバーはありません...http.get("http://example.com/yourfile.html",function(){})
mjz19910

この回答にメモリリークはありますか:stackoverflow.com/a/22793628/242933
ma11hew28

で行ったように、タイムアウトを追加できhttp.getます。メモリリークは、ファイルのダウンロードに時間がかかりすぎる場合にのみ発生します。
A-312

13

es6スタイルのプロミスベースの方法を求めて来た人にとっては、次のようになると思います。

var http = require('http');
var fs = require('fs');

function pDownload(url, dest){
  var file = fs.createWriteStream(dest);
  return new Promise((resolve, reject) => {
    var responseSent = false; // flag to make sure that response is sent only once.
    http.get(url, response => {
      response.pipe(file);
      file.on('finish', () =>{
        file.close(() => {
          if(responseSent)  return;
          responseSent = true;
          resolve();
        });
      });
    }).on('error', err => {
        if(responseSent)  return;
        responseSent = true;
        reject(err);
    });
  });
}

//example
pDownload(url, fileLocation)
  .then( ()=> console.log('downloaded file no issues...'))
  .catch( e => console.error('error while downloading', e));

2
responseSetフラグが原因で、調査する時間がなかった何らかの理由で、ファイルが不完全にダウンロードされました。エラーは表示されませんでしたが、私が入力していた.txtファイルには、そこに必要な行の半分が含まれていました。フラグのロジックを削除して修正しました。誰かがアプローチに問題を抱えているかどうかを指摘したかっただけです。それでも、+ 1
Milan Velebit 2018年

6

Vince Yuanのコードはすばらしいですが、何か問題があるようです。

function download(url, dest, callback) {
    var file = fs.createWriteStream(dest);
    var request = http.get(url, function (response) {
        response.pipe(file);
        file.on('finish', function () {
            file.close(callback); // close() is async, call callback after close completes.
        });
        file.on('error', function (err) {
            fs.unlink(dest); // Delete the file async. (But we don't check the result)
            if (callback)
                callback(err.message);
        });
    });
}

宛先フォルダを指定できますか?

6

httpとhttpsの両方を使用できるので、私はrequest()を好みます。

request('http://i3.ytimg.com/vi/J---aiyznGQ/mqdefault.jpg')
  .pipe(fs.createWriteStream('cat.jpg'))

リクエストが廃止されたようですgithub.com/request/request/issues/3142 "As of Feb 11th 2020, request is fully deprecated. No new changes are expected to land. In fact, none have landed for some time."
Michael Kubler

5
const download = (url, path) => new Promise((resolve, reject) => {
http.get(url, response => {
    const statusCode = response.statusCode;

    if (statusCode !== 200) {
        return reject('Download error!');
    }

    const writeStream = fs.createWriteStream(path);
    response.pipe(writeStream);

    writeStream.on('error', () => reject('Error writing to file!'));
    writeStream.on('finish', () => writeStream.close(resolve));
});}).catch(err => console.error(err));

5

こんにちは、child_processモジュールとcurlコマンドを使用できると思います。

const cp = require('child_process');

let download = async function(uri, filename){
    let command = `curl -o ${filename}  '${uri}'`;
    let result = cp.execSync(command);
};


async function test() {
    await download('http://zhangwenning.top/20181221001417.png', './20181221001417.png')
}

test()

さらに、大きな複数のファイルをダウンロードする場合は、クラスターモジュールを使用してより多くのCPUコアを使用できます。



4

読み取り可能なストリームを解決するpromiseを使用してダウンロードします。リダイレクトを処理するための追加のロジックを配置します。

var http = require('http');
var promise = require('bluebird');
var url = require('url');
var fs = require('fs');
var assert = require('assert');

function download(option) {
    assert(option);
    if (typeof option == 'string') {
        option = url.parse(option);
    }

    return new promise(function(resolve, reject) {
        var req = http.request(option, function(res) {
            if (res.statusCode == 200) {
                resolve(res);
            } else {
                if (res.statusCode === 301 && res.headers.location) {
                    resolve(download(res.headers.location));
                } else {
                    reject(res.statusCode);
                }
            }
        })
        .on('error', function(e) {
            reject(e);
        })
        .end();
    });
}

download('http://localhost:8080/redirect')
.then(function(stream) {
    try {

        var writeStream = fs.createWriteStream('holyhigh.jpg');
        stream.pipe(writeStream);

    } catch(e) {
        console.error(e);
    }
});

1
302はURLリダイレクトのHTTPステータスコードでもあるため、ifステートメントでこれを使用する必要があります。[301,302] .indexOf(res.statusCode)!== -1
sidanmor

質問は、サードパーティモードを含まないようにするためのものでした:)
David Gatti

3

Expressを使用している場合は、res.download()メソッドを使用します。それ以外の場合は、fsモジュールが使用します。

app.get('/read-android', function(req, res) {
   var file = "/home/sony/Documents/docs/Android.apk";
    res.download(file) 
}); 

(または)

   function readApp(req,res) {
      var file = req.fileName,
          filePath = "/home/sony/Documents/docs/";
      fs.exists(filePath, function(exists){
          if (exists) {     
            res.writeHead(200, {
              "Content-Type": "application/octet-stream",
              "Content-Disposition" : "attachment; filename=" + file});
            fs.createReadStream(filePath + file).pipe(res);
          } else {
            res.writeHead(400, {"Content-Type": "text/plain"});
            res.end("ERROR File does NOT Exists.ipa");
          }
        });  
    }

3

パイプラインを使用する場合、他のすべてのストリームを閉じ、メモリリークがないことを確認します。

作業例:

const http = require('http');
const { pipeline } = require('stream');
const fs = require('fs');

const file = fs.createWriteStream('./file.jpg');

http.get('http://via.placeholder.com/150/92c952', response => {
  pipeline(
    response,
    file,
    err => {
      if (err)
        console.error('Pipeline failed.', err);
      else
        console.log('Pipeline succeeded.');
    }
  );
});

私の答え「ストリーム上の.pipeと.pipelineの違いは何」


2

パス:imgタイプ:jpgランダムuniqid

    function resim(url) {

    var http = require("http");
    var fs = require("fs");
    var sayi = Math.floor(Math.random()*10000000000);
    var uzanti = ".jpg";
    var file = fs.createWriteStream("img/"+sayi+uzanti);
    var request = http.get(url, function(response) {
  response.pipe(file);
});

        return sayi+uzanti;
}

0

ライブラリがなければ、指摘するだけでバグが発生する可能性があります。ここにいくつかあります:

  • バイナリであるこのURL https://calibre-ebook.com/dist/portableのように、httpリダイレクトを処理できません。
  • httpモジュールはhttps urlを実行できません。 Protocol "https:" not supported.

ここに私の提案:

  • wgetまたはのようなシステムツールを呼び出すcurl
  • node-wget-promiseのようなツールを使用します。これも非常に簡単に使用できます。 var wget = require('node-wget-promise'); wget('http://nodejs.org/images/logo.svg');

0
function download(url, dest, cb) {

  var request = http.get(url, function (response) {

    const settings = {
      flags: 'w',
      encoding: 'utf8',
      fd: null,
      mode: 0o666,
      autoClose: true
    };

    // response.pipe(fs.createWriteStream(dest, settings));
    var file = fs.createWriteStream(dest, settings);
    response.pipe(file);

    file.on('finish', function () {
      let okMsg = {
        text: `File downloaded successfully`
      }
      cb(okMsg);
      file.end(); 
    });
  }).on('error', function (err) { // Handle errors
    fs.unlink(dest); // Delete the file async. (But we don't check the result)
    let errorMsg = {
      text: `Error in file downloadin: ${err.message}`
    }
    if (cb) cb(errorMsg);
  });
};

0

res.redirecthttpsファイルのダウンロードURL を使用してみると、ファイルがダウンロードされます。

お気に入り: res.redirect('https//static.file.com/file.txt');


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

var download = function(uri, filename, callback){
    request.head(uri, function(err, res, body){
    console.log('content-type:', res.headers['content-type']);
    console.log('content-length:', res.headers['content-length']);
    request(uri).pipe(fs.createWriteStream(filename)).on('close', callback);

    }); 
};   

download('https://www.cryptocompare.com/media/19684/doge.png', 'icons/taskks12.png', function(){
    console.log('done');
});

0

サードパーティに依存せずにリダイレクトを検索する別の方法を次に示します。

        var download = function(url, dest, cb) {
            var file = fs.createWriteStream(dest);
            https.get(url, function(response) {
                if ([301,302].indexOf(response.statusCode) !== -1) {
                    body = [];
                    download(response.headers.location, dest, cb);
                  }
              response.pipe(file);
              file.on('finish', function() {
                file.close(cb);  // close() is async, call cb after close completes.
              });
            });
          }

0

download.js(つまり、/ project / utils / download.js)

const fs = require('fs');
const request = require('request');

const download = (uri, filename, callback) => {
    request.head(uri, (err, res, body) => {
        console.log('content-type:', res.headers['content-type']);
        console.log('content-length:', res.headers['content-length']);

        request(uri).pipe(fs.createWriteStream(filename)).on('close', callback);
    });
};

module.exports = { download };


app.js

... 
// part of imports
const { download } = require('./utils/download');

...
// add this function wherever
download('https://imageurl.com', 'imagename.jpg', () => {
  console.log('done')
});


-4
var requestModule=require("request");

requestModule(filePath).pipe(fs.createWriteStream('abc.zip'));

5
コードダンプは一般的に役に立たないため、反対投票または削除される可能性があります。少なくともコードが将来のビジターのために何をしているのかを説明するために編集する価値があります。
バグ2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.