Node.js ile bir dosya nasıl indirilir (üçüncü taraf kitaplıkları kullanılmadan)?


443

Üçüncü taraf kitaplıkları kullanmadan Node.js ile nasıl dosya indirebilirim ?

Özel bir şeye ihtiyacım yok. Yalnızca belirli bir URL'den dosya indirmek ve daha sonra belirli bir dizine kaydetmek istiyorum.


5
"node.js ile bir dosya indir" - sunucuya yüklemek mi istiyorsunuz ? veya sunucunuzu kullanarak uzak sunucudan bir dosya almak? veya node.js sunucunuzdan indirmek için istemciye bir dosya mı sunuyorsunuz?
Joseph

66
"Sadece belirli bir url'den bir dosya indirmek ve sonra verilen bir dizine kaydetmek istiyorum," oldukça açık görünüyor. :)
Michelle Tilley

34
Joseph, tüm düğüm süreçlerinin sunucu süreçleri olduğu konusunda yanlış bir iddiada bulunuyor
lededje

1
@lededje Sunucu işleminin bir dosyayı indirmesini ve sunucudaki bir dizine kaydetmesini ne engeller? Prefectly yapılabilir.
Gherman

Yanıtlar:


598

Bir HTTP GETisteği oluşturabilir ve bunu responseyazılabilir bir dosya akışına bağlayabilirsiniz:

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);
});

Komut satırında bir hedef dosya veya dizin veya URL belirtmek gibi bilgi toplamayı desteklemek istiyorsanız, Komutan gibi bir şeye göz atın .


3
Bu senaryoyu çalıştırdığımda aşağıdaki konsol çıktısını aldı: 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) .
Anderson Green

http.getHatta farklı bir URL kullanmayı deneyin ; Belki de http://i3.ytimg.com/vi/J---aiyznGQ/mqdefault.jpg(ve değiştirme file.pngile file.jpg).
Michelle Tilley

8
Komut dosyası sona erdiğinde bu kod dosyayı düzgün kapatıyor mu yoksa veri kaybedecek mi?
philk

2
@quantumpotato İsteğinizden aldığınız yanıta bir göz atın
Michelle Tilley

6
Geleneksel hesap, bu req url türüne bağlıdır httpskullanmak gerekir httpso hata atmak olacaktır aksi.
Krishnadas PC

523

Hataları ele almayı unutmayın! Aşağıdaki kod Augusto Roman'ın cevabına dayanmaktadır.

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()kendisi pipemümkün mü?
rasx

@theGrayFox Çünkü bu cevaptaki kod kabul edilen koddan çok daha uzun. :)
pootow

2
@Abdul Kulağa node.js / javascript için çok yeni gibisiniz. Bu eğiticiye bir göz atın: tutorialspoint.com/nodejs/nodejs_callbacks_concept.htm Karmaşık değil.
Vince Yuan

1
@Abdul belki de anladığınız şey sınıfın geri kalanıyla paylaşmanız iyi olur?
Curtwagner1984

5
İndirme hızını görmenin bir yolu var mı? Gibi kaç mb / s izleyebilirsiniz? Teşekkürler!
Tino Caer

137

Michelle Tilley'in dediği gibi, ancak uygun kontrol akışıyla:

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);
    });
  });
}

finishEtkinliği beklemeden saf komut dosyaları eksik bir dosyayla sonuçlanabilir.

Düzenleme: @Augusto Roman'a , açıkça çağrılmaması cbgereken file.close, aktarılması gerektiğini işaret ettiği için teşekkürler .


3
geri arama kafamı karıştırıyor. şimdi çağırırsam download()nasıl yaparım? cbTartışma olarak neyi yerleştirirdim ? Ben var download('someURI', '/some/destination', cb)ama cb koymak ne anlamıyorum
Abdul

1
@Abdul Bir işlevi olan geri aramayı yalnızca dosya başarıyla getirildiğinde bir şey yapmanız gerektiğinde belirtirsiniz.
CatalinBerta

65

Kullanım hatalarından bahsetmişken, hata talep etmek de daha iyi dinleme. Yanıt kodunu kontrol ederek bile doğrularım. Burada sadece 200 yanıt kodu için başarı olarak kabul edilir, ancak diğer kodlar iyi olabilir.

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);
    });
};

Bu kodun göreceli sadeliğine rağmen, yerel olarak desteklenmeyen çok daha fazla protokolü (merhaba HTTPS!) İşlediğinden istek modülünü kullanmanızı tavsiye ederim http.

Bu şu şekilde yapılır:

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
İstek modülü doğrudan HTTP'ler için çalışır. Güzel!
Thiago

@ventura yep, btw, şimdi güvenli bağlantıları işleyebilen yerel https modülü de var.
Buzut

Şüphesiz daha fazla hata eğilimli. Her neyse, istek modülünü kullanmanın bir seçenek olduğu her durumda, daha yüksek ve dolayısıyla daha kolay ve verimli olduğu için tavsiye ederim.
Buzut

2
@Alex, hayır, bu bir hata mesajı ve bir dönüş var. Yani eğer response.statusCode !== 200cb üzerinde finishasla çağrılamaz .
Buzut

1
İstek modülünü kullanarak örnek gösterdiğiniz için teşekkür ederiz.
Pete Alvin

48

gfxmonk'un cevabı, geri arama ve file.close()tamamlama arasında çok sıkı bir veri yarışına sahiptir . file.close()aslında kapanma tamamlandığında çağrılan bir geri arama alır. Aksi takdirde, dosyanın hemen kullanımı başarısız olabilir (çok nadiren!).

Tam bir çözüm:

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.
    });
  });
}

Finish olayını beklemeden saf komut dosyaları eksik bir dosyayla sonuçlanabilir. cbGeri aramayı yakın plan aracılığıyla zamanlamadan, dosyaya erişme ile dosyaya gerçekten hazır olma arasında bir yarış alabilirsiniz.


2
İsteği bir değişkene ne için saklıyorsunuz?
polkovnikov.ph

varsayılan olarak global bir değişken haline gelmemesi için bir değişkeni "saklar".
philk

@philk var request =kaldırılırsa global bir değişkenin nasıl oluşturulduğunu nasıl bilirsiniz ?
ma11hew28

Haklısın, isteği kaydetmeye gerek yok, zaten kullanılmıyor. Anlatmak istediğin bu?
philk

17

Belki node.js değişti, ancak diğer çözümlerle ilgili bazı sorunlar var gibi görünüyor (v8.1.2 düğümü kullanılarak):

  1. Sen buna gerek yok file.close()içinde finisholay. Varsayılan olarak fs.createWriteStream, autoClose olarak ayarlanır: https://nodejs.org/api/fs.html#fs_fs_createwritestream_path_options
  2. file.close()yanlışlıkla çağrılmalıdır. Dosya silindiğinde ( unlink()) bu gerekli olmayabilir , ancak normalde: https://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
  3. Temp dosyası silinmedi statusCode !== 200
  4. fs.unlink() geri arama yapılmazsa (çıkış uyarısı)
  5. Eğer destdosya var; geçersiz kılındı

Aşağıda bu sorunları ele alan değiştirilmiş bir çözüm (ES6 ve vaatler kullanarak) bulunmaktadır.

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
Bu konuda iki yorum: 1) muhtemelen dizeleri değil Hata nesnelerini reddetmelidir, 2) fs.unlink sessizce yapmak istediğiniz şey olmayabilir hataları yutacak
Richard Nienaber

1
Harika çalışıyor! URL'leriniz HTTPS kullanan olursa, sadece yerine const https = require("https");içinconst http = require("http");
Russ

15

Zaman aşımı ile çözüm, bellek sızıntısını önleyin:

Aşağıdaki kod Brandon Tilley'in cevabına dayanmaktadır:

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();
    });
});

Bir hata aldığınızda dosya oluşturmayın ve X saniyeden sonra isteğinizi kapatmak için zaman aşımını kullanmayı tercih edin.


1
Bu sadece bir dosya, indirmek için hiçbir protokol veya sunucu yok ...http.get("http://example.com/yourfile.html",function(){})
mjz19910

Bu cevapta bellek sızıntısı var mı: stackoverflow.com/a/22793628/242933 ?
ma11hew28

Benim yaptığım gibi zaman aşımı ekleyebilirsiniz http.get. Bellek sızıntısı yalnızca dosyanın indirilmesi çok uzun sürerse.
A-312

13

es6 tarzı vaat tabanlı bir yol arayanlar için, sanırım böyle bir şey olurdu:

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
responseSetbayrak, araştırmak için zamanım olmamış bir sebepten dolayı, dosyamın eksik indirilmesine neden oldu. Hiçbir hata attı ama ben dolduruyordu .txt dosya orada olması gereken satırların yarısı vardı. Bayrak mantığını kaldırmak düzeltildi. Sadece birisi yaklaşım ile ilgili sorunları varsa işaret etmek istedim. Hala, +1
Milan Velebit

6

Vince Yuan'ın kodu harika ama yanlış bir şey gibi görünüyor.

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);
        });
    });
}

hedef klasörü belirleyebilir miyiz?

6

Ben onunla hem http hem de https kullanabilirsiniz çünkü request () tercih ederim.

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

Görünüşe göre İstek kullanımdan kaldırıldı 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

Merhaba , Sanırım child_process modülünü ve curl komutunu kullanabilirsiniz.

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()

Ayrıca, large birden fazla dosya indirmek istediğinizde, daha fazla işlemci çekirdeği kullanmak için küme modülünü kullanabilirsiniz.



4

Okunabilir bir akışı çözen bir söz kullanarak indirin. yönlendirmeyi işlemek için ekstra mantık ekleyin.

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 ayrıca URL yönlendirmesi için HTTP durum kodudur, bu nedenle if ifadesinde [301,302] .indexOf (res.statusCode)! == -1 kullanmalısınız
sidanmor

Sorular üçüncü taraf modlarını içermeyecek şekilde spesifikti :)
David Gatti

3

Ekspres kullanıyorsanız res.download () yöntemini kullanın. aksi takdirde fs modül kullanımı.

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

(veya)

   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

PipBu şekilde boru hattı kullanırsanız , diğer tüm akışları kapatır ve bellek sızıntısı olmadığından emin olur.

Çalışma örneği:

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.');
    }
  );
});

Gönderen cevabım için "akışlarında .pipe ve .pipeline arasındaki fark nedir" .


2

Yol: img tipi: jpg rastgele 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

Kütüphane olmadan sadece işaret etmek buggy olabilir. Burda biraz var:

İşte benim önerim:

  • Sistem aracını wgetveyacurl
  • kullanımı çok basit olan node-wget-promise gibi bir araç kullanın. 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

Kullanmayı deneyebilirsiniz res.redirectHttps dosyası indirme url'sini ve ardından dosyayı indirirsiniz.

Sevmek: 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

Üçüncü taraf bağımlılığı olmadan ve yönlendirmeleri aramanın başka bir yolu:

        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 (yani /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
Kod dökümleri genellikle yararlı değildir ve indirilemez veya silinebilir. En azından gelecekteki ziyaretçiler için kodun ne yaptığını açıklamak önemlidir.
Bugs
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.