Node.js ile görüntü indirme [kapalı]


169

Node.js kullanarak görüntüleri indirmek için bir komut dosyası yazmaya çalışıyorum. Şimdiye kadar sahip olduğum şey bu:

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

Ancak bunu daha sağlam hale getirmek istiyorum:

  1. Bunu ve bunu daha iyi yapan kütüphaneler var mı?
  2. Yanıt başlıklarının yalan söyleme şansı var mı (yaklaşık uzunluk, içerik türü hakkında)?
  3. İlgilenmem gereken başka durum kodları var mı? Yönlendirmelerle uğraşmalı mıyım?
  4. binaryKodlamanın kullanımdan kaldırılacağı bir yerde okuduğumu düşünüyorum . O zaman ne yapacağım?
  5. Bunu pencerelerde nasıl çalıştırabilirim?
  6. Bu komut dosyasını daha iyi hale getirmenin başka yolları var mı?

Neden: kullanıcıların bana bir URL verebileceği imgur benzeri bir özellik için, bu görüntüyü indiriyorum ve görüntüyü birden çok boyutta yeniden barındırıyorum.

Yanıtlar:


401

İstek modülünü kullanmanızı öneririm . Bir dosyayı indirmek aşağıdaki kod kadar basittir:

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
Güzel! İndirmeden önce boyutu ve içerik türünü kontrol etmenin bir yolu var mı?
Jonathan Ong

2
Görüntüleri nereye indiriyor?
Gofilord

18
Benim için çalışmıyor (Resim bozuk
Darth

2
@Gofilord, görüntüyü kök dizininize indirir.
dang

1
Kaydedildikleri yeri değiştirebilir misiniz? Onları belirli bir klasörde isteseydiniz?
AKL012

34

Birkaç gün önce bu problemle karşılaştım, saf bir NodeJS cevabı için, parçaları birleştirmek için Stream'i kullanmanızı öneririm.

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

En yeni Düğüm sürümleri ikili dizelerle iyi çalışmaz, bu nedenle dizelerle parçaları birleştirmek ikili verilerle çalışırken iyi bir fikir değildir.

* 'Data.read ()' kullanırken dikkatli olun, sonraki 'read ()' işlemi için akışı boşaltacaktır. Bir kereden fazla kullanmak istiyorsanız, bir yerde saklayın.


7
İndirmeyi neden doğrudan diske aktarmıyorsunuz?
geon

bozuk bir dosya oluştururken dizeleri parçalama ile ilgili birçok sorun vardı, ama bu yaptı
Shaho

28

Sen kullanabilirsiniz AXIOS (bir sözünü bir in seçtiğiniz sırayla indirme görüntülere node.js için tabanlı HTTP istemci) asenkron ortamda :

npm i axios

Ardından, resimleri indirmeye başlamak için aşağıdaki temel örneği kullanabilirsiniz:

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
Harika bir örnek! Ancak zar zor okunabilir kod, standart stili deneyin : D
camwhite

3
@camwhite Noktalı virgülleri tercih ederim . ;)
Grant Miller

1
Gerçekten 'bitirme' ve 'hata' olaylarını yazma akışına iliştirmeli, bir Söz'e sarmalı ve sözü geri vermelisiniz. Aksi takdirde, henüz tamamen indirilmemiş bir resme erişmeyi deneyebilirsiniz.
jwerre

Beklemek, erişmeye çalışmadan önce görüntünün tamamen indirildiğinden emin olmaz mı? @jwerre
FabricioG

@jwerre @FabricioG download_imageİade edilen söz için 'bitiş' ve 'hata' olayını yakalama işlevini güncelledim
Beeno Tung

10

ilerleme indirmek istiyorsanız bunu deneyin:

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

nasıl kullanılır:

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

not: hem istek hem de istek ilerleme modüllerini aşağıdakileri kullanarak kurmalısınız:

npm install request request-progress --save

2
Bu harika çalıştı, ancak bir statusCodeçek eklemeyi önermek istedi . Örneğin 500 statusCode öğesine çarpmaz 'on("error", e). Bir ekleyerek on('response', (response) => console.error(response.statusCode))hata ayıklamayı büyük ölçüde kolaylaştırır,
mateuscb

1
Cevabımı düzenleyebilirsiniz :)
Fareed Alnamrouti

4

Yukarıdakilere dayanarak, herhangi birinin yazma / okuma akışlarındaki hataları işlemesi gerekiyorsa, bu sürümü kullandım. Not stream.read()biz okuma ve tetiği bitirmek böylece bir yazma hatası durumunda, gerekli olduğunu closeokuma akışı.

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()modası geçmiş gibi görünüyor, bir hata atıyornot 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
fonksiyonunuz geri aramayı tetiklemiyor
crockpotveggies

4

Bu Cezary'nin cevabının bir uzantısıdır. Belirli bir dizine indirmek istiyorsanız bunu kullanın. Ayrıca var yerine const kullanın. Bu şekilde güvenli.

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