web-dev-qa-db-fra.com

Télécharger des images avec node.js

J'essaie d'écrire un script pour télécharger des images à l'aide de node.js. Voici ce que j'ai jusqu'à présent:

var maxLength = 10 // 10mb
var download = function(uri, callback) {
  http.request(uri)
    .on('response', function(res) {
      if (res.headers['content-length'] > maxLength*1024*1024) {
        callback(new Error('Image too large.'))
      } else if (!~[200, 304].indexOf(res.statusCode)) {
        callback(new Error('Received an invalid status code.'))
      } else if (!res.headers['content-type'].match(/image/)) {
        callback(new Error('Not an image.'))
      } else {
        var body = ''
        res.setEncoding('binary')
        res
          .on('error', function(err) {
            callback(err)
          })
          .on('data', function(chunk) {
            body += chunk
          })
          .on('end', function() {
            // What about Windows?!
            var path = '/tmp/' + Math.random().toString().split('.').pop()
            fs.writeFile(path, body, 'binary', function(err) {
              callback(err, path)
            })
          })
      }
    })
    .on('error', function(err) {
      callback(err)
    })
    .end();
}

Je veux cependant rendre cela plus robuste:

  1. Y a-t-il des bibliothèques qui font cela et le font mieux?
  2. Y a-t-il une chance que les en-têtes de réponse mentent (sur la longueur, sur le type de contenu)?
  3. Existe-t-il d'autres codes d'état dont je devrais me soucier? Devrais-je m'embêter avec des redirections?
  4. Je pense avoir lu quelque part que binary l'encodage va être déconseillé. Qu'est-ce que je fais alors?
  5. Comment puis-je obtenir que cela fonctionne sur Windows?
  6. Y a-t-il d'autres moyens d'améliorer ce script?

Pourquoi: pour une fonctionnalité similaire à imgur où les utilisateurs peuvent me donner une URL, je télécharge cette image et la réhéberge en plusieurs tailles.

141
Jonathan Ong

Je suggérerais d'utiliser le module de requête . Télécharger un fichier est aussi simple que le code suivant:

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.google.com/images/srpr/logo3w.png', 'google.png', function(){
  console.log('done');
});
348

Je me suis heurté à ce problème il y a quelques jours. Pour une réponse pure à NodeJS, je suggérerais d'utiliser Stream pour fusionner les morceaux.

var http = require('http'),                                                
    Stream = require('stream').Transform,                                  
    fs = require('fs');                                                    

var url = 'http://www.google.com/images/srpr/logo11w.png';                    

http.request(url, function(response) {                                        
  var data = new Stream();                                                    

  response.on('data', function(chunk) {                                       
    data.Push(chunk);                                                         
  });                                                                         

  response.on('end', function() {                                             
    fs.writeFileSync('image.png', data.read());                               
  });                                                                         
}).end();

Les dernières versions de Node ne fonctionneront pas bien avec les chaînes binaires. La fusion de morceaux avec des chaînes n'est donc pas une bonne idée lorsque vous travaillez avec des données binaires.

* Soyez prudent lorsque vous utilisez 'data.read ()', cela videra le flux pour la prochaine opération 'read ()'. Si vous souhaitez l'utiliser plus d'une fois, stockez-le quelque part.

22
Nihey Takizawa

Vous pouvez utiliser Axios (un client HTTP à promis - pour Node.js) à télécharger images dans l'ordre de votre choix dans un environnement asynchrone :

npm i axios

Ensuite, vous pouvez utiliser l'exemple de base suivant pour commencer à télécharger des images:

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

/* ============================================================
  Function: Download Image
============================================================ */

const download_image = (url, image_path) =>
  axios({
    url,
    responseType: 'stream',
  }).then(
    response =>
      new Promise((resolve, reject) => {
        response.data
          .pipe(fs.createWriteStream(image_path))
          .on('finish', () => resolve())
          .on('error', e => reject(e));
      }),
  );

/* ============================================================
  Download Images in Order
============================================================ */

(async () => {
  let example_image_1 = await download_image('https://example.com/test-1.png', 'example-1.png');

  console.log(example_image_1.status); // true
  console.log(example_image_1.error); // ''

  let example_image_2 = await download_image('https://example.com/does-not-exist.png', 'example-2.png');

  console.log(example_image_2.status); // false
  console.log(example_image_2.error); // 'Error: Request failed with status code 404'

  let example_image_3 = await download_image('https://example.com/test-3.png', 'example-3.png');

  console.log(example_image_3.status); // true
  console.log(example_image_3.error); // ''
})();
10
Grant Miller

si vous voulez télécharger le progrès, essayez ceci:

var fs = require('fs');
var request = require('request');
var progress = require('request-progress');

module.exports = function (uri, path, onProgress, onResponse, onError, onEnd) {
    progress(request(uri))
    .on('progress', onProgress)
    .on('response', onResponse)
    .on('error', onError)
    .on('end', onEnd)
    .pipe(fs.createWriteStream(path))
};

comment utiliser:

  var download = require('../lib/download');
  download("https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png", "~/download/logo.png", function (state) {
            console.log("progress", state);
        }, function (response) {
            console.log("status code", response.statusCode);
        }, function (error) {
            console.log("error", error);
        }, function () {
            console.log("done");
        });

remarque: vous devez installer les deux modules request et request-progress en utilisant:

npm install request request-progress --save
9

S'appuyant sur ce qui précède, si quelqu'un a besoin de gérer des erreurs dans les flux d'écriture/lecture, j'ai utilisé cette version. Notez que stream.read() en cas d'erreur d'écriture, il est nécessaire pour pouvoir terminer la lecture et déclencher close sur le flux de lecture.

var download = function(uri, filename, callback){
  request.head(uri, function(err, res, body){
    if (err) callback(err, filename);
    else {
        var stream = request(uri);
        stream.pipe(
            fs.createWriteStream(filename)
                .on('error', function(err){
                    callback(error, filename);
                    stream.read();
                })
            )
        .on('close', function() {
            callback(null, filename);
        });
    }
  });
};
4
VladFr
var fs = require('fs'),
http = require('http'),
https = require('https');

var Stream = require('stream').Transform;

var downloadImageToUrl = (url, filename, callback) => {

    var client = http;
    if (url.toString().indexOf("https") === 0){
      client = https;
     }

    client.request(url, function(response) {                                        
      var data = new Stream();                                                    

      response.on('data', function(chunk) {                                       
         data.Push(chunk);                                                         
      });                                                                         

      response.on('end', function() {                                             
         fs.writeFileSync(filename, data.read());                               
      });                                                                         
   }).end();
};

downloadImageToUrl('https://www.google.com/images/srpr/logo11w.png', 'public/uploads/users/abc.jpg');
4
Chandan Chhajer

Ceci est une extension de la réponse de Cezary. Si vous voulez le télécharger dans un répertoire spécifique, utilisez ceci. Aussi, utilisez const au lieu de var. C'est sûr de cette façon.

const fs = require('fs');
const request = require('request');
var download = function(uri, filename, callback){
  request.head(uri, function(err, res, body){    
    request(uri).pipe(fs.createWriteStream(filename)).on('close', callback);
  });
};

download('https://www.google.com/images/srpr/logo3w.png', './images/google.png', function(){
  console.log('done');
});
3
Ahsan Ahmed