Pobieranie obrazów za pomocą node.js [zamknięte]


169

Próbuję napisać skrypt do pobierania obrazów za pomocą node.js. Oto, co mam do tej pory:

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

Chcę jednak uczynić to solidniejszym:

  1. Czy są biblioteki, które robią to i robią to lepiej?
  2. Czy jest szansa, że ​​nagłówki odpowiedzi kłamią (o długości, o typie treści)?
  3. Czy są jakieś inne kody statusu, które powinny mnie zainteresować? Czy powinienem zawracać sobie głowę przekierowaniami?
  4. Myślę, że gdzieś przeczytałem, że binarykodowanie zostanie wycofane. Co mam wtedy zrobić?
  5. Jak mogę to uruchomić w systemie Windows?
  6. Czy są jakieś inne sposoby na ulepszenie tego skryptu?

Dlaczego: w przypadku funkcji podobnej do imgur, w której użytkownicy mogą podać mi adres URL, pobieram ten obraz i ponownie hostuję obraz w wielu rozmiarach.

Odpowiedzi:


401

Sugerowałbym użycie modułu żądań . Pobieranie pliku jest tak proste, jak poniższy kod:

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
Chłodny! Czy istnieje sposób sprawdzenia rozmiaru i typu zawartości przed pobraniem?
Jonathan Ong

2
Skąd pobiera obrazy?
Gofilord

17
Nie działa dla mnie (obraz uszkodzony
Darth

2
@Gofilord pobiera obraz do katalogu głównego.
dang

1
Czy możesz zmienić lokalizację, w której są zapisywane? Gdybyś chciał je mieć w określonym folderze?
AKL012

34

Napotkałem ten problem kilka dni temu, aby uzyskać czystą odpowiedź NodeJS, sugerowałbym użycie Stream do scalenia fragmentów razem.

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

Najnowsze wersje Node nie będą dobrze współpracować z ciągami binarnymi, więc łączenie fragmentów z ciągami nie jest dobrym pomysłem podczas pracy z danymi binarnymi.

* Po prostu bądź ostrożny używając 'data.read ()', spowoduje to opróżnienie strumienia dla następnej operacji 'read ()'. Jeśli chcesz go użyć więcej niż raz, przechowuj go gdzieś.


7
Dlaczego nie przesyłać strumieniowo pobierania bezpośrednio na dysk?
geon

miał wiele problemów z dzieleniem ciągów znaków, ponieważ utworzył uszkodzony plik, ale to się udało
Shaho

27

Możesz użyć Axios ( klienta HTTP opartego na obietnicy dla Node.js) do pobierania obrazów w wybranej kolejności w środowisku asynchronicznym :

npm i axios

Następnie możesz skorzystać z następującego podstawowego przykładu, aby rozpocząć pobieranie obrazów:

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
Świetny przykład! Ale ledwo czytelny kod, wypróbuj standardowy styl: D
camwhite

3
@camwhite Wolę średniki . ;)
Grant Miller,

1
Naprawdę powinieneś dołączyć zdarzenia „finish” i „error” do strumienia zapisu, opakować je w Obietnicę i zwrócić obietnicę. W przeciwnym razie możesz spróbować uzyskać dostęp do obrazu, który nie został jeszcze w całości pobrany.
jwerre

Czy oczekiwanie nie zapewniłoby całkowitego pobrania obrazu przed próbą uzyskania dostępu? @jwerre
FabricioG

@jwerre @FabricioG Zaktualizowałem funkcję, download_imageaby przechwytywać zdarzenie „zakończenie” i „błąd” dla zwróconej obietnicy
Beeno Tung

10

jeśli chcesz postęp pobierania, spróbuj tego:

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

jak używać:

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

uwaga: należy zainstalować oba moduły request i request-progress przy użyciu:

npm install request request-progress --save

2
To działało świetnie, ale chciał zasugerować dodanie statusCodeczeku. Na przykład kod statusu 500 nie trafi do 'on("error", e). Dodanie on('response', (response) => console.error(response.statusCode))
znacznika

1
Możesz edytować moją odpowiedź :)
Fareed Alnamrouti

4

Opierając się na powyższym, jeśli ktoś potrzebuje obsługi błędów w strumieniach zapisu / odczytu, użyłem tej wersji. Zwróć uwagę, że stream.read()w przypadku błędu zapisu jest to wymagane, abyśmy mogli zakończyć odczyt i wyzwolić closestrumień odczytu.

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()wydaje się być nieaktualny, wyrzuca błądnot 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
twoja funkcja nie wyzwala wywołania zwrotnego
crockpotveggies

4

To jest rozszerzenie odpowiedzi Cezarego. Jeśli chcesz pobrać go do określonego katalogu, użyj tego. Użyj również const zamiast var. W ten sposób jest bezpieczny.

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');
});
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.