Descarga de imágenes con node.js

Intento escribir un script para descargar imágenes usando node.js. Esto es lo que tengo hasta ahora:

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

Yo, sin embargo, quiero hacer esto más robusto:

  1. ¿Hay bibliotecas que hacen esto y lo hacen mejor?
  2. ¿Existe la posibilidad de que los encabezados de respuesta mientan (sobre la longitud, sobre el tipo de contenido)?
  3. ¿Hay algún otro código de estado que me interese? ¿Debería molestarme con los redireccionamientos?
  4. Creo que leí en algún lado que la encoding binary va a ser desaprobada. ¿Qué hago entonces?
  5. ¿Cómo puedo hacer que esto funcione en Windows?
  6. ¿Alguna otra forma de mejorar este script?

Por qué: para una función similar a imgur, donde los usuarios pueden darme una URL, descargo esa imagen y la identifico en varios tamaños.

Sugeriría usar el módulo de solicitud . Descargar un archivo es tan simple como el siguiente código:

 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('http://sofes.miximages.com/image/logo3w.png', 'google.png', function(){ console.log('done'); }); 

Me encontré con este problema hace unos días, para una respuesta pura de NodeJS, sugeriría usar Stream para unir los fragmentos.

 var http = require('http'), Stream = require('stream').Transform, fs = require('fs'); var url = 'http://sofes.miximages.com/image/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(); 

Las versiones más nuevas de Nodo no funcionarán bien con cadenas binarias, por lo que combinar trozos con cadenas no es una buena idea cuando se trabaja con datos binarios.

* Solo tenga cuidado al usar ‘data.read ()’, va a vaciar la secuencia para la siguiente operación ‘read ()’. Si desea usarlo más de una vez, guárdelo en algún lugar.

Sobre la base de lo anterior, si alguien necesita manejar errores en las secuencias de escritura / lectura, utilicé esta versión. Tenga en cuenta que stream.read() en caso de error de escritura, es necesario para que podamos terminar de leer y disparar de close en la secuencia de lectura.

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

si quieres descargar el progreso prueba esto:

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

cómo utilizar:

  var download = require('../lib/download'); download("http://sofes.miximages.com/image/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: debe instalar los módulos solicitud y progreso de solicitud usando:

 npm install request request-progress --save 

Puede usar Axios (un cliente HTTP basado en promesas para Node.js) para descargar imágenes en el orden que elija en un entorno asincrónico :

 npm i axios 

Luego, puede usar el siguiente ejemplo básico para comenzar a descargar imágenes:

 const fs = require( 'fs' ); const axios = require( 'axios' ); /* ============================================================ Function: Download Image ============================================================ */ const download_image = ( url, image_path ) => axios( { 'url' : url, 'responseType' : 'stream' } ).then( response => { response.data.pipe( fs.createWriteStream( image_path ) ); return { 'status' : true, 'error' : '' }; }).catch( error => ( { 'status' : false, 'error' : 'Error: ' + error.message } ) ); /* ============================================================ 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 ); // '' })(); 
 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('http://sofes.miximages.com/image/logo11w.png', 'public/uploads/users/abc.jpg'); 

Esta es una extensión de la respuesta de Cezary. Si desea descargarlo a un directorio específico, use esto. Además, use const en lugar de var. Es seguro de esta manera.

 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('http://sofes.miximages.com/image/logo3w.png', './images/google.png', function(){ console.log('done'); });