Mengunduh gambar dengan node.js [ditutup]

169

Saya mencoba menulis skrip untuk mengunduh gambar menggunakan node.js. Inilah yang saya miliki sejauh ini:

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

Namun, saya ingin membuatnya lebih kuat:

  1. Apakah ada perpustakaan yang melakukan ini dan melakukan ini dengan lebih baik?
  2. Apakah ada kemungkinan tajuk respons berbohong (tentang panjang, tentang jenis konten)?
  3. Apakah ada kode status lain yang harus saya pedulikan? Haruskah saya repot dengan pengalihan?
  4. Saya pikir saya membaca suatu tempat bahwa binarypengkodean akan ditinggalkan. Apa yang harus saya lakukan?
  5. Bagaimana saya bisa membuatnya bekerja di windows?
  6. Adakah cara lain untuk membuat skrip ini lebih baik?

Mengapa: untuk fitur yang mirip dengan imgur di mana pengguna dapat memberi saya URL, saya mengunduh gambar itu, dan memasang ulang gambar dalam berbagai ukuran.

Jonathan Ong
sumber

Jawaban:

401

Saya sarankan menggunakan modul permintaan . Mengunduh file sesederhana kode berikut:

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');
});
Cezary Wojtkowski
sumber
1
Keren! Apakah ada cara untuk memeriksa ukuran dan jenis konten sebelum benar-benar mengunduhnya?
Jonathan Ong
2
Di mana ia mengunduh gambar?
Gofilord
17
Tidak berfungsi untuk saya (Gambar rusak
Darth
2
@Gofilord, unduh gambar ke direktori root Anda.
dang
1
Bisakah Anda mengubah lokasi penyimpanannya? Jika Anda menginginkannya di folder tertentu?
AKL012
34

Saya mengalami masalah ini beberapa hari yang lalu, untuk jawaban murni NodeJS saya sarankan menggunakan Stream untuk menggabungkan potongan-potongan bersama.

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

Versi Node terbaru tidak akan bekerja dengan baik dengan string biner, jadi menggabungkan potongan dengan string bukanlah ide yang baik ketika bekerja dengan data biner.

* Berhati-hatilah saat menggunakan 'data.read ()', itu akan mengosongkan aliran untuk operasi 'baca ()' berikutnya. Jika Anda ingin menggunakannya lebih dari sekali, simpan di suatu tempat.

Nihey Takizawa
sumber
7
Mengapa tidak mengalirkan unduhan langsung ke disk?
geon
punya banyak masalah dengan chunking string bersama karena membuat file yang korup, tapi ini berhasil
Shaho
28

Anda dapat menggunakan Axios ( klien HTTP berbasis janji untuk Node.js) untuk mengunduh gambar sesuai urutan pilihan Anda di lingkungan asinkron :

npm i axios

Kemudian, Anda dapat menggunakan contoh dasar berikut untuk mulai mengunduh gambar:

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); // ''
})();
Grant Miller
sumber
2
Contoh yang bagus! Tetapi kode hampir tidak terbaca, coba gaya standar : D
camwhite
3
@camwhite Saya lebih suka titik koma . ;)
Grant Miller
1
Anda benar-benar harus melampirkan acara 'selesai' dan 'kesalahan' ke stream tulis, bungkus dalam Janji dan kembalikan janji. Kalau tidak, Anda dapat mencoba mengakses gambar yang belum diunduh sepenuhnya.
jwerre
Tidakkah penantian akan memastikan unduhan gambar sepenuhnya sebelum mencoba mengakses? @jwerre
FabricioG
@jwerre @FabricioG Saya telah memperbarui fungsi download_imageuntuk menangkap acara 'selesai' dan 'kesalahan' untuk janji yang dikembalikan
Beeno Tung
10

jika Anda ingin kemajuan unduh coba ini:

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

Cara Penggunaan:

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

Catatan: Anda harus menginstal modul permintaan & permintaan-kemajuan menggunakan:

npm install request request-progress --save
Fareed Alnamrouti
sumber
2
Ini bekerja dengan baik, tetapi ingin menyarankan menambahkan statusCodecek. StatusCode 500 misalnya, tidak akan menekan 'on("error", e). Dengan menambahkan on('response', (response) => console.error(response.statusCode))itu sangat memudahkan debugging,
mateuscb
1
Anda dapat mengedit jawaban saya :)
Fareed Alnamrouti
4

Membangun di atas, jika ada yang perlu menangani kesalahan dalam menulis / membaca aliran, saya menggunakan versi ini. Catat stream.read()jika terjadi kesalahan tulis, itu diperlukan agar kami dapat selesai membaca dan memicu closepada aliran baca.

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);
        });
    }
  });
};
VladFr
sumber
2
stream.read()tampaknya sudah ketinggalan zaman, melempar kesalahannot 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');
Chandan Chhajer
sumber
1
fungsi Anda tidak memicu callback
crockpotveggies
4

Ini adalah perpanjangan dari jawaban Cezary. Jika Anda ingin mengunduhnya ke direktori tertentu, gunakan ini. Juga, gunakan const bukan var. Aman seperti ini.

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