Download di immagini con node.js [chiuso]


169

Sto provando a scrivere uno script per scaricare immagini usando node.js. Questo è quello che ho finora:

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

Tuttavia, voglio renderlo più robusto:

  1. Ci sono librerie che lo fanno e lo fanno meglio?
  2. Esiste la possibilità che si trovino le intestazioni di risposta (sulla lunghezza, sul tipo di contenuto)?
  3. Ci sono altri codici di stato di cui dovrei preoccuparmi? Dovrei preoccuparmi dei reindirizzamenti?
  4. Penso di aver letto da qualche parte che la binarycodifica sarà deprecata. Cosa faccio allora?
  5. Come posso farlo funzionare su Windows?
  6. Altri modi per migliorare questo script?

Perché: per una funzione simile a imgur in cui gli utenti possono fornirmi un URL, scarico quell'immagine e rehosto l'immagine in più dimensioni.

Risposte:


401

Suggerirei di utilizzare il modulo di richiesta . Il download di un file è semplice come il seguente codice:

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

1
Freddo! C'è un modo per controllare le dimensioni e il tipo di contenuto prima di scaricarlo effettivamente?
Jonathan Ong,

2
Dove scarica le immagini?
Gofilord,

17
Non funziona per me (Immagine corrotta
Darth

2
@Gofilord scarica l'immagine nella directory principale.
dang,

1
Puoi cambiare la posizione in cui sono salvati? Se li volevi in ​​una cartella specifica?
AKL012,

34

Ho riscontrato questo problema alcuni giorni fa, per una pura risposta NodeJS suggerirei di usare Stream per unire i blocchi insieme.

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

Le versioni del nodo più recenti non funzionano bene con le stringhe binarie, quindi l'unione di blocchi con stringhe non è una buona idea quando si lavora con dati binari.

* Fai solo attenzione quando usi 'data.read ()', svuoterà il flusso per la prossima operazione 'read ()'. Se vuoi usarlo più di una volta, conservalo da qualche parte.


7
Perché non eseguire lo streaming del download direttamente sul disco?
geon,

ha avuto molti problemi con le stringhe di chunking insieme mentre creava un file corrotto, ma lo ha fatto
Shaho,

28

È possibile utilizzare Axios (un client HTTP basato su promesse per Node.js) per scaricare le immagini nell'ordine scelto in un ambiente asincrono :

npm i axios

Quindi, è possibile utilizzare il seguente esempio di base per iniziare a scaricare le immagini:

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

2
Ottimo esempio! Ma codice a malapena leggibile, prova lo stile standard : D
camwhite,

3
@camwhite Preferisco i punti e virgola . ;)
Grant Miller,

1
Dovresti davvero allegare eventi 'finish' ed 'error' al flusso di scrittura, racchiuderli in una Promessa e restituire la promessa. Altrimenti potresti provare ad accedere a un'immagine che non è stata ancora completamente scaricata.
jwerre,

L'attesa non si assicurerebbe che le immagini vengano scaricate completamente prima di provare ad accedere? @jwerre
FabricioG

@jwerre @FabricioG Ho aggiornato la funzione download_imageper acquisire l'evento 'finish' e 'error' per la promessa restituita
Beeno Tung,

10

se vuoi scaricare i progressi prova questo:

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

come usare:

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

nota: è necessario installare entrambi i moduli di richiesta e avanzamento della richiesta utilizzando:

npm install request request-progress --save

2
Funzionava alla grande, ma volevo suggerire di aggiungere un statusCodeassegno. Un 500 statusCode, ad esempio, non colpirà il 'on("error", e). Aggiungendo un on('response', (response) => console.error(response.statusCode))
elemento

1
Puoi modificare la mia risposta :)
Fareed Alnamrouti,

4

Sulla base di quanto sopra, se qualcuno ha bisogno di gestire errori nei flussi di scrittura / lettura, ho usato questa versione. Nota stream.read()in caso di errore di scrittura, è necessario in modo da poter terminare la lettura e il trigger closesul flusso di lettura.

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

2
stream.read()sembra essere obsoleto, genera un errorenot a function
bendulum

4
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');

1
la tua funzione non attiva il callback
crockpotveggies

4

Questa è un'estensione della risposta di Cezary. Se vuoi scaricarlo in una directory specifica, usa questo. Inoltre, usa const anziché var. È sicuro in questo modo.

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');
});
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.