Завантаження зображень за допомогою node.js [закрито]


169

Я намагаюся написати сценарій для завантаження зображень за допомогою node.js. Ось що я маю досі:

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

Я, однак, хочу зробити це більш надійним:

  1. Чи є бібліотеки, які роблять це, і роблять це краще?
  2. Чи є шанс, що заголовки відповідей лежать (про довжину, про тип вмісту)?
  3. Чи є якісь інші статусні коди, про які я повинен дбати? Чи варто турбуватися про переадресації?
  4. Думаю, я десь прочитав, що binaryкодування буде застарілим. Що я роблю тоді?
  5. Як я можу змусити це працювати на windows?
  6. Будь-якими іншими способами можна зробити цей сценарій кращим?

Чому: для функції, подібної до imgur, коли користувачі можуть надати мені URL-адресу, я завантажую це зображення і повторно готую зображення в декількох розмірах.

Відповіді:


401

Я б запропонував використовувати модуль запиту . Завантажити файл так само просто, як і наступний код:

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
Класно! Чи є спосіб перевірити розмір і тип вмісту, перш ніж його фактично завантажувати?
Джонатан Онг

2
Куди він завантажує зображення?
Гофілорд

17
Не працює для мене (Зображення зіпсоване
Дарт

2
@Gofilord завантажує зображення у свій кореневий каталог.
dang

1
Чи можете ви змінити місце, де вони зберігаються? Якщо ви хотіли їх у певній папці?
AKL012

34

Я зіткнувся з цією проблемою кілька днів тому, для чистої відповіді NodeJS я б запропонував використовувати Stream, щоб об'єднати шматки разом.

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

Найновіші версії Node не будуть добре працювати з двійковими рядками, тому об’єднання фрагментів із рядками не є хорошою ідеєю при роботі з бінарними даними.

* Будьте обережні, використовуючи "data.read ()", він спорожнить потік для наступної операції "read ()". Якщо ви хочете використовувати його не один раз, зберігайте його десь.


7
Чому б не передати завантаження безпосередньо на диск?
геон

У мене було багато проблем із збиванням рядків разом, оскільки він створив пошкоджений файл, але це вдалося
Шахо,

27

Ви можете використовувати AXIOSобіцянку заснованого HTTP клієнта для Node.js) для завантаження зображень в порядку за своїм вибором в якості асинхронної середовища :

npm i axios

Потім ви можете використовувати наступний основний приклад, щоб розпочати завантаження зображень:

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
Чудовий приклад! Але ледь читабельний код, спробуйте стандартний стиль: D
camwhite

3
@camwhite Я вважаю за краще крапки з комою . ;)
Грант Міллер

1
Ви дійсно повинні прикріпити події "закінчення" та "помилки" до потоку запису, загорнути їх у Обіцянку та повернути обіцянку. Інакше ви можете спробувати отримати доступ до зображення, яке ще не було повністю завантажено.
jwerre

Чи не очікував би переконатися, що зображення завантажується повністю, перш ніж спробувати отримати доступ? @jwerre
FabricioG

@jwerre @FabricioG Я оновив функцію download_imageзйомки події "фінішу" та "помилки" для повернутої обіцянки
Beeno Tung

10

якщо ви хочете прогресу скачати, спробуйте це:

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

як використовувати:

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

Примітка. Ви повинні встановити обидва модулі запит і запит, використовуючи:

npm install request request-progress --save

2
Це спрацювало чудово, але хотіли запропонувати додати statusCodeчек. Наприклад, код 500 статусу не вплине на 'on("error", e). Додавши це, on('response', (response) => console.error(response.statusCode))це значно полегшує налагодження,
mateuscb

1
Ви можете редагувати мою відповідь :)
Fareed Alnamrouti

4

Спираючись на вищезазначене, якщо комусь потрібно обробляти помилки в потоках запису / читання, я використав цю версію. Зауважте, що stream.read()у випадку помилки запису це потрібно, щоб ми могли закінчити читання та запустити closeв потоці читання.

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()видається застарілим, not a function
припускає

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
Ваша функція не викликає зворотного дзвінка
crockpotveggies

4

Це продовження відповіді Цезарі. Якщо ви хочете завантажити його в певний каталог, використовуйте це. Також використовуйте const замість var. Це безпечно таким чином.

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');
});
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.