"descargar un archivo con node.js" : ¿quiere decir subir al servidor? o recuperar un archivo de un servidor remoto usando su servidor? o servir un archivo a un cliente para descargarlo de su servidor node.js?
Joseph
66
"Solo quiero descargar un archivo de una URL dada y luego guardarlo en un directorio dado", parece bastante claro. :)
Michelle Tilley
34
Joseph está haciendo una afirmación incorrecta de que todos los procesos de nodo son procesos de servidor
lededje
1
@lededje ¿Qué impide que un proceso del servidor descargue un archivo y lo guarde en un directorio de un servidor? Es perfectamente factible.
Gherman
Respuestas:
598
Puede crear una GETsolicitud HTTP y canalizarla responseen una secuencia de archivo grabable:
Si desea admitir la recopilación de información en la línea de comandos, como especificar un archivo o directorio de destino o URL, consulte algo como Commander .
Tengo la siguiente salida de la consola cuando me encontré con este script: node.js:201 throw e; // process.nextTick error, or 'error' event on first tick ^ Error: connect ECONNREFUSED at errnoException (net.js:646:11) at Object.afterConnect [as oncomplete] (net.js:637:18) .
Anderson Green
Intente usar una URL diferente en la http.getlínea; tal vez http://i3.ytimg.com/vi/J---aiyznGQ/mqdefault.jpg(y reemplazar file.pngcon file.jpg).
Michelle Tilley
8
¿Este código cierra el archivo correctamente cuando finaliza el script o perdería datos?
philk
2
@quantumpotato Eche un vistazo a la respuesta que está recibiendo de su solicitud
Michelle Tilley
66
Esto depende del tipo de URL de solicitud si está solicitando httpsque deba usar, de lo httpscontrario arrojará un error.
Krishnadas PC
523
¡No olvides manejar los errores! El siguiente código se basa en la respuesta de Augusto Roman.
var http = require('http');var fs = require('fs');var download =function(url, dest, cb){var file = fs.createWriteStream(dest);var request = http.get(url,function(response){
response.pipe(file);
file.on('finish',function(){
file.close(cb);// close() is async, call cb after close completes.});}).on('error',function(err){// Handle errors
fs.unlink(dest);// Delete the file async. (But we don't check the result)if(cb) cb(err.message);});};
la devolución de llamada me está confundiendo. si ahora invoco download(), ¿cómo lo haría? ¿Qué pondría como cbargumento? Tengo el download('someURI', '/some/destination', cb)pero no entiendo qué poner en el CB
Abdul
1
@Abdul Usted especifica la devolución de llamada con una función solo si necesita hacer algo cuando el archivo se ha recuperado correctamente.
CatalinBerta
65
Hablando de errores de manejo, es aún mejor escuchar los errores de solicitud también. Incluso validaría comprobando el código de respuesta. Aquí se considera exitoso solo para el código de respuesta 200, pero otros códigos pueden ser buenos.
const fs = require('fs');const http = require('http');const download =(url, dest, cb)=>{const file = fs.createWriteStream(dest);const request = http.get(url,(response)=>{// check if response is successif(response.statusCode !==200){return cb('Response status was '+ response.statusCode);}
response.pipe(file);});// close() is async, call cb after close completes
file.on('finish',()=> file.close(cb));// check for request error too
request.on('error',(err)=>{
fs.unlink(dest);return cb(err.message);});
file.on('error',(err)=>{// Handle errors
fs.unlink(dest);// Delete the file async. (But we don't check the result) return cb(err.message);});};
A pesar de la relativa simplicidad de este código, recomendaría usar el módulo de solicitud, ya que maneja muchos más protocolos (¡Hola HTTPS!) Que no son compatibles de forma nativa http.
Eso se haría así:
const fs = require('fs');const request = require('request');const download =(url, dest, cb)=>{const file = fs.createWriteStream(dest);const sendReq = request.get(url);// verify response code
sendReq.on('response',(response)=>{if(response.statusCode !==200){return cb('Response status was '+ response.statusCode);}
sendReq.pipe(file);});// close() is async, call cb after close completes
file.on('finish',()=> file.close(cb));// check for request errors
sendReq.on('error',(err)=>{
fs.unlink(dest);return cb(err.message);});
file.on('error',(err)=>{// Handle errors
fs.unlink(dest);// Delete the file async. (But we don't check the result)return cb(err.message);});};
El módulo de solicitud simplemente funciona directamente para HTTP. ¡Frio!
Thiago C. S Ventura
@ventura sí, por cierto, también está el módulo https nativo que ahora puede manejar conexiones seguras.
Buzut
Es más propenso a errores sin lugar a dudas. De todos modos, en cualquier caso donde usar el módulo de solicitud es una opción, lo recomendaría ya que es un nivel mucho más alto y, por lo tanto, más fácil y eficiente.
Buzut
2
@ Alex, no, este es un mensaje de error y hay un retorno. Entonces, si nunca se llamará a response.statusCode !== 200cb on finish.
Buzut
1
Gracias por mostrar un ejemplo utilizando el módulo de solicitud.
Pete Alvin el
48
La respuesta de gfxmonk tiene una carrera de datos muy ajustada entre la devolución de llamada y la file.close()finalización. file.close()en realidad toma una devolución de llamada que se llama cuando se completa el cierre. De lo contrario, los usos inmediatos del archivo pueden fallar (¡muy raramente!).
Una solución completa es:
var http = require('http');var fs = require('fs');var download =function(url, dest, cb){var file = fs.createWriteStream(dest);var request = http.get(url,function(response){
response.pipe(file);
file.on('finish',function(){
file.close(cb);// close() is async, call cb after close completes.});});}
Sin esperar el evento final, los guiones ingenuos pueden terminar con un archivo incompleto. Sin programar la cbdevolución de llamada a través del cierre, puede obtener una carrera entre acceder al archivo y el archivo que realmente está listo.
Dos comentarios sobre esto: 1) probablemente debería rechazar objetos de Error, no cadenas, 2) fs.unlink tragará silenciosamente errores que podrían no ser necesariamente lo que quieres hacer
Richard Nienaber
1
¡Esto funciona muy bien! Y si sus URL utilizan HTTPS, simplemente sustituir const https = require("https");porconst http = require("http");
Russ
15
Solución con tiempo de espera, evitar pérdida de memoria:
El siguiente código se basa en la respuesta de Brandon Tilley:
Puede agregar tiempo de espera como lo hice en http.get. La pérdida de memoria es solo si el archivo tarda demasiado en descargarse.
A-312
13
para aquellos que vinieron en busca de una forma basada en promesas de estilo es6, supongo que sería algo como:
var http = require('http');var fs = require('fs');function pDownload(url, dest){var file = fs.createWriteStream(dest);returnnewPromise((resolve, reject)=>{var responseSent =false;// flag to make sure that response is sent only once.
http.get(url, response =>{
response.pipe(file);
file.on('finish',()=>{
file.close(()=>{if(responseSent)return;
responseSent =true;
resolve();});});}).on('error', err =>{if(responseSent)return;
responseSent =true;
reject(err);});});}//example
pDownload(url, fileLocation).then(()=> console.log('downloaded file no issues...')).catch( e => console.error('error while downloading', e));
responseSetLa bandera causó, por alguna razón que no había tenido tiempo de investigar, que mi archivo se descargara de forma incompleta. No aparecieron errores, pero el archivo .txt que estaba rellenando tenía la mitad de las filas que necesitaban estar allí. Eliminar la lógica de la bandera lo arregló. Solo quería señalar eso si alguien tenía problemas con el enfoque. Aún así, +1
Milan Velebit
6
El código de Vince Yuan es genial, pero parece estar mal.
function download(url, dest, callback){var file = fs.createWriteStream(dest);var request = http.get(url,function(response){
response.pipe(file);
file.on('finish',function(){
file.close(callback);// close() is async, call callback after close completes.});
file.on('error',function(err){
fs.unlink(dest);// Delete the file async. (But we don't check the result)if(callback)
callback(err.message);});});}
Parece que la solicitud ha quedado en desuso github.com/request/request/issues/3142"As of Feb 11th 2020, request is fully deprecated. No new changes are expected to land. In fact, none have landed for some time."
302 también es un código de estado HTTP para la redirección de URL, por lo que debe usar este [301,302] .indexOf (res.statusCode)! == -1 en la instrucción if
sidanmor
Las preguntas eran específicas para no incluir modos de terceros :)
David Gatti
3
Si está utilizando el método express use res.download (). de lo contrario, uso del módulo fs.
el módulo http no puede https url, obtendrá Protocol "https:" not supported.
Aquí mi sugerencia:
Llame a la herramienta del sistema como wgetocurl
use alguna herramienta como node-wget-promise que también es muy fácil de usar.
var wget = require('node-wget-promise');
wget('http://nodejs.org/images/logo.svg');
Aquí hay otra forma de manejarlo sin dependencia de terceros y también buscando redireccionamientos:
var download =function(url, dest, cb){var file = fs.createWriteStream(dest);
https.get(url,function(response){if([301,302].indexOf(response.statusCode)!==-1){
body =[];
download(response.headers.location, dest, cb);}
response.pipe(file);
file.on('finish',function(){
file.close(cb);// close() is async, call cb after close completes.});});}
...// part of importsconst{ download }= require('./utils/download');...// add this function wherever
download('https://imageurl.com','imagename.jpg',()=>{
console.log('done')});
Los volcados de código generalmente no son útiles y se pueden rechazar o eliminar. Valdría la pena editar al menos para explicar qué está haciendo el código para los futuros visitantes.
Respuestas:
Puede crear una
GET
solicitud HTTP y canalizarlaresponse
en una secuencia de archivo grabable:Si desea admitir la recopilación de información en la línea de comandos, como especificar un archivo o directorio de destino o URL, consulte algo como Commander .
fuente
node.js:201 throw e; // process.nextTick error, or 'error' event on first tick ^ Error: connect ECONNREFUSED at errnoException (net.js:646:11) at Object.afterConnect [as oncomplete] (net.js:637:18)
.http.get
línea; tal vezhttp://i3.ytimg.com/vi/J---aiyznGQ/mqdefault.jpg
(y reemplazarfile.png
confile.jpg
).https
que deba usar, de lohttps
contrario arrojará un error.¡No olvides manejar los errores! El siguiente código se basa en la respuesta de Augusto Roman.
fuente
download()
propiopipe
poder?Como dijo Michelle Tilley, pero con el flujo de control apropiado:
Sin esperar el
finish
evento, los guiones ingenuos pueden terminar con un archivo incompleto.Editar: Gracias a @Augusto Roman por señalar que se
cb
debe pasar afile.close
, no se llama explícitamente.fuente
download()
, ¿cómo lo haría? ¿Qué pondría comocb
argumento? Tengo eldownload('someURI', '/some/destination', cb)
pero no entiendo qué poner en el CBHablando de errores de manejo, es aún mejor escuchar los errores de solicitud también. Incluso validaría comprobando el código de respuesta. Aquí se considera exitoso solo para el código de respuesta 200, pero otros códigos pueden ser buenos.
A pesar de la relativa simplicidad de este código, recomendaría usar el módulo de solicitud, ya que maneja muchos más protocolos (¡Hola HTTPS!) Que no son compatibles de forma nativa
http
.Eso se haría así:
fuente
response.statusCode !== 200
cb onfinish
.La respuesta de gfxmonk tiene una carrera de datos muy ajustada entre la devolución de llamada y la
file.close()
finalización.file.close()
en realidad toma una devolución de llamada que se llama cuando se completa el cierre. De lo contrario, los usos inmediatos del archivo pueden fallar (¡muy raramente!).Una solución completa es:
Sin esperar el evento final, los guiones ingenuos pueden terminar con un archivo incompleto. Sin programar la
cb
devolución de llamada a través del cierre, puede obtener una carrera entre acceder al archivo y el archivo que realmente está listo.fuente
var request =
se elimina?Quizás node.js ha cambiado, pero parece que hay algunos problemas con las otras soluciones (usando el nodo v8.1.2):
file.close()
en elfinish
evento. Por defecto, elfs.createWriteStream
está configurado en autoClose: https://nodejs.org/api/fs.html#fs_fs_createwritestream_path_optionsfile.close()
debe ser llamado por error. Quizás esto no sea necesario cuando se elimina el archivo (unlink()
), pero normalmente es: https://nodejs.org/api/stream.html#stream_readable_pipe_destination_optionsstatusCode !== 200
fs.unlink()
sin una devolución de llamada está en desuso (advertencia de salida)dest
archivo existe; está anuladoA continuación se muestra una solución modificada (usando ES6 y promesas) que maneja estos problemas.
fuente
const https = require("https");
porconst http = require("http");
Solución con tiempo de espera, evitar pérdida de memoria:
El siguiente código se basa en la respuesta de Brandon Tilley:
No haga un archivo cuando reciba un error, y prefiera usar el tiempo de espera para cerrar su solicitud después de X segundos.
fuente
http.get("http://example.com/yourfile.html",function(){})
http.get
. La pérdida de memoria es solo si el archivo tarda demasiado en descargarse.para aquellos que vinieron en busca de una forma basada en promesas de estilo es6, supongo que sería algo como:
fuente
responseSet
La bandera causó, por alguna razón que no había tenido tiempo de investigar, que mi archivo se descargara de forma incompleta. No aparecieron errores, pero el archivo .txt que estaba rellenando tenía la mitad de las filas que necesitaban estar allí. Eliminar la lógica de la bandera lo arregló. Solo quería señalar eso si alguien tenía problemas con el enfoque. Aún así, +1El código de Vince Yuan es genial, pero parece estar mal.
fuente
Prefiero request () porque puedes usar http y https con él.
fuente
"As of Feb 11th 2020, request is fully deprecated. No new changes are expected to land. In fact, none have landed for some time."
fuente
Hola, creo que puedes usar el módulo child_process y el comando curl.
Además, cuando desee descargar grandes archivos múltiples, puede usar el módulo de clúster para usar más núcleos de CPU.
fuente
Puede usar https://github.com/douzi8/ajax-request#download
fuente
ajax-request
no es una biblioteca de terceros?Descargue usando promesa, que resuelve una secuencia legible. poner lógica extra para manejar la redirección.
fuente
Si está utilizando el método express use res.download (). de lo contrario, uso del módulo fs.
(o)
fuente
De mi respuesta a "¿Cuál es la diferencia entre .pipe y .pipeline en streams" .
fuente
Ruta: tipo img: jpg random uniqid
fuente
Sin biblioteca, podría tener errores solo para señalar. Aquí hay algunos:
Protocol "https:" not supported.
Aquí mi sugerencia:
wget
ocurl
var wget = require('node-wget-promise'); wget('http://nodejs.org/images/logo.svg');
fuente
fuente
Puede intentar usar
res.redirect
la URL de descarga del archivo https y luego descargará el archivo.Me gusta:
res.redirect('https//static.file.com/file.txt');
fuente
fuente
Aquí hay otra forma de manejarlo sin dependencia de terceros y también buscando redireccionamientos:
fuente
download.js (es decir, /project/utils/download.js)
app.js
fuente
Podemos usar el módulo de nodo de descarga y es muy simple, consulte a continuación https://www.npmjs.com/package/download
fuente
fuente