Comment télécharger un fichier avec Node.js (sans utiliser de bibliothèques tierces)?
443
Comment télécharger un fichier avec Node.js sans utiliser de bibliothèques tierces ?
Je n'ai besoin de rien de spécial. Je souhaite uniquement télécharger un fichier à partir d'une URL donnée, puis l'enregistrer dans un répertoire donné.
"télécharger un fichier avec node.js" - voulez-vous dire télécharger sur le serveur? ou récupérer un fichier à partir d'un serveur distant en utilisant votre serveur? ou servir un fichier à un client pour le télécharger à partir de votre serveur node.js?
Joseph
67
"Je veux seulement télécharger un fichier à partir d'une URL donnée, puis l'enregistrer dans un répertoire donné", cela semble assez clair. :)
Michelle Tilley
34
Joseph fait une affirmation incorrecte que tous les processus de noeud sont des processus serveur
lededje
1
@lededje Qu'est-ce qui empêche un processus serveur de télécharger un fichier et de l'enregistrer dans un répertoire sur un serveur? C'est parfaitement faisable.
Gherman
Réponses:
598
Vous pouvez créer une GETdemande HTTP et la diriger responsevers un flux de fichiers accessible en écriture:
Si vous souhaitez prendre en charge la collecte d'informations sur la ligne de commande - comme spécifier un fichier ou un répertoire cible, ou une URL - consultez quelque chose comme Commander .
Je suis la sortie de la console suivante quand je courais ce 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
Essayez d'utiliser une URL différente sur la http.getligne; peut-être http://i3.ytimg.com/vi/J---aiyznGQ/mqdefault.jpg(et remplacer file.pngpar file.jpg).
Michelle Tilley
8
Ce code ferme-t-il correctement le fichier à la fin du script ou perdrait-il des données?
philk
2
@quantumpotato Jetez un œil à la réponse que vous obtenez de votre demande
Michelle Tilley
6
Cela dépend du type d'URL de demande si vous demandez, httpsvous devez utiliser httpssinon il générera une erreur.
Krishnadas PC
523
N'oubliez pas de gérer les erreurs! Le code suivant est basé sur la réponse d'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);});};
le rappel me déroute. si j'invoque maintenant download(), comment ferais-je? Que placerais-je comme cbargument? J'ai le download('someURI', '/some/destination', cb)mais je ne comprends pas quoi mettre dans le cb
Abdul
1
@Abdul Vous spécifiez le rappel avec une fonction uniquement si vous devez faire quelque chose lorsque le fichier a été récupéré avec succès.
CatalinBerta
65
En parlant de gestion des erreurs, il vaut mieux écouter aussi les erreurs de demande. Je validerais même en vérifiant le code de réponse. Ici, il est considéré comme un succès uniquement pour 200 codes de réponse, mais d'autres codes peuvent être bons.
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);});};
Malgré la relative simplicité de ce code, je conseillerais d'utiliser le module de requête car il gère beaucoup plus de protocoles (bonjour HTTPS!) Qui ne sont pas supportés nativement par http.
Cela se ferait ainsi:
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);});};
Le module de demande fonctionne directement pour les HTTP. Cool!
Thiago C. S Ventura
@ventura yep, btw, il y a aussi le module https natif qui peut désormais gérer les connexions sécurisées.
Buzut
C'est plus sujet aux erreurs sans aucun doute. Quoi qu'il en soit, dans tous les cas où l'utilisation du module de demande est une option, je le conseillerais car c'est un niveau beaucoup plus élevé et donc, plus facile et efficace.
Buzut
2
@Alex, non, c'est un message d'erreur et il y a un retour. Donc, si response.statusCode !== 200le cb on finishne sera jamais appelé.
Buzut
1
Merci de montrer l'exemple en utilisant le module de demande.
Pete Alvin
48
La réponse de gfxmonk a une course de données très serrée entre le rappel et la file.close()fin. file.close()prend en fait un rappel qui est appelé lorsque la fermeture est terminée. Sinon, les utilisations immédiates du fichier peuvent échouer (très rarement!).
Une solution complète est:
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.});});}
Sans attendre l'événement de fin, les scripts naïfs peuvent se retrouver avec un fichier incomplet. Sans planifier le cbrappel via la fermeture, vous pouvez obtenir une course entre l'accès au fichier et le fichier réellement prêt.
Deux commentaires à ce sujet: 1) il devrait probablement rejeter les objets d'erreur, pas les chaînes, 2) fs.unlink avalera tranquillement des erreurs qui ne sont pas nécessairement ce que vous voulez faire
Richard Nienaber
1
Cela fonctionne très bien! Et si vos URL utilisent HTTPS, remplacez simplement const https = require("https");parconst http = require("http");
Russ
15
Solution avec timeout, évite les fuites de mémoire:
Le code suivant est basé sur la réponse de Brandon Tilley:
Vous pouvez ajouter un délai d'expiration comme je l'ai fait dans http.get. La fuite de mémoire ne se produit que si le fichier prend trop de temps pour être téléchargé.
A-312
13
pour ceux qui sont venus à la recherche de promesses basées sur le style es6, je suppose que ce serait quelque chose comme:
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));
responseSetflag a causé, pour une raison que je n'avais pas eu le temps d'examiner, mon fichier à télécharger de manière incomplète. Aucune erreur n'est apparue mais le fichier .txt que je remplissais avait la moitié des lignes qui devaient être là. La suppression de la logique du drapeau l'a corrigé. Je voulais juste signaler si quelqu'un avait des problèmes avec l'approche. Toujours, +1
Milan Velebit
6
Le code de Vince Yuan est génial mais il semble que quelque chose ne va pas.
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);});});}
On dirait que Request a été déconseillé 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."
De plus, lorsque vous souhaitez télécharger de gros fichiers multiples, vous pouvez utiliser le module de cluster pour utiliser plus de cœurs de processeur.
302 est également le code d'état HTTP pour la redirection d'URL, vous devez donc utiliser ce [301,302] .indexOf (res.statusCode)! == -1 dans l'instruction if
sidanmor
Les questions étaient spécifiques pour ne pas inclure les modes tiers :)
David Gatti
3
Si vous utilisez la méthode express use res.download (). sinon utilisation du module fs.
Le module http ne peut pas https url, vous obtiendrez Protocol "https:" not supported.
Voici ma suggestion:
Outil système d'appel comme wgetoucurl
utilisez un outil comme node-wget-promise qui est également très simple à utiliser.
var wget = require('node-wget-promise');
wget('http://nodejs.org/images/logo.svg');
Voici encore une autre façon de le gérer sans dépendance tierce et de rechercher également des redirections:
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')});
Les vidages de code ne sont généralement pas utiles et peuvent être rétrogradés ou supprimés. Il vaudrait la peine d'éditer pour au moins expliquer ce que le code fait pour les futurs visiteurs.
Réponses:
Vous pouvez créer une
GET
demande HTTP et la dirigerresponse
vers un flux de fichiers accessible en écriture:Si vous souhaitez prendre en charge la collecte d'informations sur la ligne de commande - comme spécifier un fichier ou un répertoire cible, ou une URL - consultez quelque chose comme Commander .
la source
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
ligne; peut-êtrehttp://i3.ytimg.com/vi/J---aiyznGQ/mqdefault.jpg
(et remplacerfile.png
parfile.jpg
).https
vous devez utiliserhttps
sinon il générera une erreur.N'oubliez pas de gérer les erreurs! Le code suivant est basé sur la réponse d'Augusto Roman.
la source
download()
lui - mêmepipe
capable?Comme l'a dit Michelle Tilley, mais avec le flux de contrôle approprié:
Sans attendre l'
finish
événement, les scripts naïfs peuvent se retrouver avec un fichier incomplet.Edit: Merci à @Augusto Roman d'avoir souligné qui
cb
devrait être transmis àfile.close
, pas appelé explicitement.la source
download()
, comment ferais-je? Que placerais-je commecb
argument? J'ai ledownload('someURI', '/some/destination', cb)
mais je ne comprends pas quoi mettre dans le cbEn parlant de gestion des erreurs, il vaut mieux écouter aussi les erreurs de demande. Je validerais même en vérifiant le code de réponse. Ici, il est considéré comme un succès uniquement pour 200 codes de réponse, mais d'autres codes peuvent être bons.
Malgré la relative simplicité de ce code, je conseillerais d'utiliser le module de requête car il gère beaucoup plus de protocoles (bonjour HTTPS!) Qui ne sont pas supportés nativement par
http
.Cela se ferait ainsi:
la source
response.statusCode !== 200
le cb onfinish
ne sera jamais appelé.La réponse de gfxmonk a une course de données très serrée entre le rappel et la
file.close()
fin.file.close()
prend en fait un rappel qui est appelé lorsque la fermeture est terminée. Sinon, les utilisations immédiates du fichier peuvent échouer (très rarement!).Une solution complète est:
Sans attendre l'événement de fin, les scripts naïfs peuvent se retrouver avec un fichier incomplet. Sans planifier le
cb
rappel via la fermeture, vous pouvez obtenir une course entre l'accès au fichier et le fichier réellement prêt.la source
var request =
est supprimée?Peut-être que node.js a changé, mais il semble qu'il y ait des problèmes avec les autres solutions (en utilisant le nœud v8.1.2):
file.close()
lors de l'finish
événement. Par défaut, lefs.createWriteStream
est défini sur autoClose: https://nodejs.org/api/fs.html#fs_fs_createwritestream_path_optionsfile.close()
doit être appelé en cas d'erreur. Peut-être que ce n'est pas nécessaire lorsque le fichier est supprimé (unlink()
), mais normalement c'est: https://nodejs.org/api/stream.html#stream_readable_pipe_destination_optionsstatusCode !== 200
fs.unlink()
sans rappel est obsolète (affiche un avertissement)dest
fichier existe; il est annuléVous trouverez ci-dessous une solution modifiée (utilisant ES6 et promesses) qui gère ces problèmes.
la source
const https = require("https");
parconst http = require("http");
Solution avec timeout, évite les fuites de mémoire:
Le code suivant est basé sur la réponse de Brandon Tilley:
Ne créez pas de fichier lorsque vous obtenez une erreur et préférez utiliser le délai d'expiration pour fermer votre demande après X secondes.
la source
http.get("http://example.com/yourfile.html",function(){})
http.get
. La fuite de mémoire ne se produit que si le fichier prend trop de temps pour être téléchargé.pour ceux qui sont venus à la recherche de promesses basées sur le style es6, je suppose que ce serait quelque chose comme:
la source
responseSet
flag a causé, pour une raison que je n'avais pas eu le temps d'examiner, mon fichier à télécharger de manière incomplète. Aucune erreur n'est apparue mais le fichier .txt que je remplissais avait la moitié des lignes qui devaient être là. La suppression de la logique du drapeau l'a corrigé. Je voulais juste signaler si quelqu'un avait des problèmes avec l'approche. Toujours, +1Le code de Vince Yuan est génial mais il semble que quelque chose ne va pas.
la source
Je préfère request () car vous pouvez utiliser à la fois http et https.
la source
"As of Feb 11th 2020, request is fully deprecated. No new changes are expected to land. In fact, none have landed for some time."
la source
Salut , Je pense que vous pouvez utiliser le module child_process et la commande curl.
De plus, lorsque vous souhaitez télécharger de gros fichiers multiples, vous pouvez utiliser le module de cluster pour utiliser plus de cœurs de processeur.
la source
Vous pouvez utiliser https://github.com/douzi8/ajax-request#download
la source
ajax-request
n'est pas une bibliothèque tierce?Téléchargez à l'aide de promise, qui résout un flux lisible. mettre une logique supplémentaire pour gérer la redirection.
la source
Si vous utilisez la méthode express use res.download (). sinon utilisation du module fs.
(ou)
la source
De ma réponse à "Quelle est la différence entre .pipe et .pipeline sur les flux" .
la source
Chemin: type img: jpg uniqid aléatoire
la source
Sans bibliothèque, il pourrait être bogué pour le signaler. Voici quelques-uns:
Protocol "https:" not supported.
Voici ma suggestion:
wget
oucurl
var wget = require('node-wget-promise'); wget('http://nodejs.org/images/logo.svg');
la source
la source
Vous pouvez essayer d'utiliser
res.redirect
l'URL de téléchargement du fichier https, puis il téléchargera le fichier.Comme:
res.redirect('https//static.file.com/file.txt');
la source
la source
Voici encore une autre façon de le gérer sans dépendance tierce et de rechercher également des redirections:
la source
download.js (ie /project/utils/download.js)
app.js
la source
Nous pouvons utiliser le module de nœud de téléchargement et son très simple, veuillez vous référer ci-dessous https://www.npmjs.com/package/download
la source
la source