web-dev-qa-db-fra.com

Comment télécharger un fichier avec Node.js (sans utiliser de bibliothèques tierces)?

Comment télécharger un fichier avec Node.js sans utiliser de bibliothèques tierces ?

Je n'ai besoin de rien de spécial. Je veux seulement télécharger un fichier à partir d'une URL donnée, puis l'enregistrer dans un répertoire donné.

323
greepow

Vous pouvez créer une demande HTTP GET et diriger sa response dans un flux de fichiers accessible en écriture:

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

Si vous souhaitez prendre en charge la collecte d'informations sur la ligne de commande, telle que la spécification d'un fichier ou d'un répertoire cible, ou d'une URL, recherchez un élément tel que Commander .

438
Michelle Tilley

N'oubliez pas de gérer les erreurs! Le code suivant est basé sur la réponse d'Augusto Roman.

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);
  });
};
446
Vince Yuan

Comme l'a dit Brandon Tilley, mais avec le flux de contrôle approprié:

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

Sans attendre l'événement finish, les scripts naïfs peuvent se retrouver avec un fichier incomplet.

Edit: Merci à @Augusto Roman d'avoir signalé que cb doit être passé à file.close, non appelé explicitement.

121
gfxmonk

En parlant d’erreurs de gestion, il est encore plus intéressant d’écouter les erreurs de requête. Je validerais même en vérifiant le code de réponse. Ici, le succès n’est considéré que pour 200 codes de réponse, mais d’autres codes pourraient être utiles. 

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

Malgré la relative simplicité de ce code, je conseillerais d'utiliser le module request car il gère beaucoup plus de protocoles (hello HTTPS!) Qui ne sont pas supportés nativement par http.

Cela se ferait comme suit:

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

la réponse de gfxmonk a une course de données très serrée entre le rappel et le file.close() complétant. file.close() prend en réalité un rappel qui est appelé lorsque la fermeture est terminée. Sinon, les utilisations immédiates du fichier risquent d’échouer (très rarement!).

Une solution complète c'est:

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

Sans attendre l'événement de fin, les scripts naïfs peuvent se retrouver avec un fichier incomplet. Sans planifier le rappel cb via close, vous risquez de perdre du temps entre l'accès au fichier et le fait que le fichier est prêt.

39
Augusto Roman

Node.js a peut-être changé, mais il semble y avoir quelques problèmes avec les autres solutions (en utilisant node v8.1.2):

  1. Vous n'avez pas besoin d'appeler file.close() dans l'événement finish. Par défaut, fs.createWriteStream est défini sur autoClose: https://nodejs.org/api/fs.html#fs_fs_createwritestream_path_options
  2. file.close() devrait être appelé en cas d'erreur. Cela n’est peut-être pas nécessaire lorsque le fichier est supprimé (unlink()), mais normalement il s’agit: https://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
  3. Le fichier temporaire n'est pas supprimé sur statusCode !== 200
  4. fs.unlink() sans rappel est obsolète (avertissement de sortie)
  5. Si le fichier dest existe; il est écrasé

Vous trouverez ci-dessous une solution modifiée (à l’aide de ES6 et de promesses) qui résout ces problèmes.

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);
            }
        });
    });
}
13
Bjarke Pjedsted

pour ceux qui sont venus à la recherche d'une manière basée sur la promesse de style es6, je suppose que ce serait quelque chose comme: 

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));
13
mido

Solution avec délai d'attente, éviter les fuites de mémoire:

Le code suivant est basé sur la réponse de 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();
    });
});

Ne créez pas de fichier lorsque vous recevez une erreur et préférez utiliser timeout pour fermer votre demande après X secondes.

13
A-312
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));
4
kayz1

Le code de Vince Yuan est génial, mais il semble que quelque chose ne va pas.

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

Vous pouvez utiliser https://github.com/douzi8/ajax-request#download

request.download('http://res.m.ctrip.com/html5/Content/images/57.png', 
  function(err, res, body) {}
);
3
douzi

Si vous utilisez exprès, utilisez la méthode res.download (). sinon, utiliser le module fs.

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

(ou)

   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");
          }
        });  
    }
2
KARTHIKEYAN.A

Chemin: img Type: jpg Aléatoire 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;
}
1
databilim

Téléchargez en utilisant promesse, qui résolvent un flux lisible. mettre de la logique supplémentaire pour gérer la redirection.

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
wdanxna

Bonjour , Je pense que vous pouvez utiliser child_process module and curl command.

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

De plus, lorsque vous souhaitez télécharger de gros fichiers multiples, vous pouvez utiliser cluster module pour utiliser davantage de cœurs de processeur.

0
wenningzhang
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
Alex Pilugin

Vous pouvez essayer d'utiliser res.redirect dans l'URL de téléchargement du fichier https, puis le fichier sera téléchargé.

Comme: res.redirect('https//static.file.com/file.txt');

0
Yin

Sans bibliothèque, il pourrait être difficile de signaler. Voici quelques-uns:

  • Impossible de gérer la redirection http, comme cette URL https://calibre-ebook.com/dist/portable qui est binaire.
  • le module http ne peut pas https url, vous obtiendrez Protocol "https:" not supported.

Voici ma suggestion:

  • Appelez l'outil système comme wget ou curl
  • utilisez un outil comme node-wget-promise qui est également très simple à utiliser. var wget = require('node-wget-promise'); wget('http://nodejs.org/images/logo.svg');
0
Geng Jiawen
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
Pankaj