Come faccio a scaricare un file con Node.js senza utilizzare librerie di terze parti ?
Non ho bisogno di niente di speciale. Voglio solo scaricare un file da un determinato URL e quindi salvarlo in una determinata directory.
Come faccio a scaricare un file con Node.js senza utilizzare librerie di terze parti ?
Non ho bisogno di niente di speciale. Voglio solo scaricare un file da un determinato URL e quindi salvarlo in una determinata directory.
Risposte:
È possibile creare una GET
richiesta HTTP e reindirizzarla response
in un flusso di file scrivibile:
const http = require('http');
const fs = require('fs');
const file = fs.createWriteStream("file.jpg");
const request = http.get("http://i3.ytimg.com/vi/J---aiyznGQ/mqdefault.jpg", function(response) {
response.pipe(file);
});
Se desideri supportare la raccolta di informazioni sulla riga di comando, ad esempio specificando un file o una directory di destinazione o un URL, controlla qualcosa come Commander .
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
linea; forse http://i3.ytimg.com/vi/J---aiyznGQ/mqdefault.jpg
(e sostituirlo file.png
con file.jpg
).
https
che devi usare https
altrimenti genererà un errore.
Non dimenticare di gestire gli errori! Il seguente codice si basa sulla risposta di 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);
});
};
download()
stesso in pipe
grado?
Come diceva Michelle Tilley, ma con il flusso di controllo appropriato:
var http = require('http');
var fs = require('fs');
var download = function(url, dest, cb) {
var file = fs.createWriteStream(dest);
http.get(url, function(response) {
response.pipe(file);
file.on('finish', function() {
file.close(cb);
});
});
}
Senza attendere l' finish
evento, gli script ingenui potrebbero finire con un file incompleto.
Modifica: Grazie a @Augusto Roman per aver sottolineato che cb
dovrebbe essere passato file.close
, non chiamato esplicitamente.
download()
, come lo farei? Cosa collocherei come cb
argomento? Ho il download('someURI', '/some/destination', cb)
ma non capisco cosa mettere nel cb
Parlando di errori di gestione, è ancora meglio ascoltare anche gli errori di richiesta. Verificherei anche controllando il codice di risposta. Qui è considerato un successo solo per 200 codici di risposta, ma altri codici potrebbero essere buoni.
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 success
if (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);
});
};
Nonostante la relativa semplicità di questo codice, consiglierei di usare il modulo di richiesta in quanto gestisce molti più protocolli (ciao HTTPS!) Che non sono supportati nativamente da http
.
Sarebbe fatto così:
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);
});
};
response.statusCode !== 200
il cb on finish
non verrà mai chiamato.
La risposta di gfxmonk ha una corsa di dati molto stretta tra il callback e il file.close()
completamento. file.close()
accetta effettivamente un callback che viene chiamato al termine della chiusura. Altrimenti, gli usi immediati del file potrebbero non riuscire (molto raramente!).
Una soluzione completa è:
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.
});
});
}
Senza attendere l'evento di fine, gli script ingenui potrebbero finire con un file incompleto. Senza pianificare la cb
richiamata alla chiusura, è possibile ottenere una corsa tra l'accesso al file e il file effettivamente pronto.
var request =
viene rimossa una variabile globale ?
Forse node.js è cambiato, ma sembra che ci siano alcuni problemi con le altre soluzioni (usando il nodo v8.1.2):
file.close()
in finish
evento. Per impostazione predefinita, fs.createWriteStream
è impostato su AutoClose: https://nodejs.org/api/fs.html#fs_fs_createwritestream_path_optionsfile.close()
dovrebbe essere chiamato in caso di errore. Forse questo non è necessario quando il file viene eliminato ( unlink()
), ma normalmente lo è: https://nodejs.org/api/stream.html#stream_readable_pipe_destination_optionsstatusCode !== 200
fs.unlink()
senza callback è deprecato (avviso output)dest
file esiste; è ignoratoDi seguito è una soluzione modificata (utilizzando ES6 e promesse) che gestisce questi problemi.
const http = require("http");
const fs = require("fs");
function download(url, dest) {
return new Promise((resolve, reject) => {
const file = fs.createWriteStream(dest, { flags: "wx" });
const request = http.get(url, response => {
if (response.statusCode === 200) {
response.pipe(file);
} else {
file.close();
fs.unlink(dest, () => {}); // Delete temp file
reject(`Server responded with ${response.statusCode}: ${response.statusMessage}`);
}
});
request.on("error", err => {
file.close();
fs.unlink(dest, () => {}); // Delete temp file
reject(err.message);
});
file.on("finish", () => {
resolve();
});
file.on("error", err => {
file.close();
if (err.code === "EEXIST") {
reject("File already exists");
} else {
fs.unlink(dest, () => {}); // Delete temp file
reject(err.message);
}
});
});
}
const https = require("https");
perconst http = require("http");
Il seguente codice si basa sulla risposta di Brandon Tilley:
var http = require('http'),
fs = require('fs');
var request = http.get("http://example12345.com/yourfile.html", function(response) {
if (response.statusCode === 200) {
var file = fs.createWriteStream("copy.html");
response.pipe(file);
}
// Add timeout.
request.setTimeout(12000, function () {
request.abort();
});
});
Non creare file quando viene visualizzato un errore e preferisci utilizzare il timeout per chiudere la richiesta dopo X secondi.
http.get("http://example.com/yourfile.html",function(){})
http.get
. La perdita di memoria è solo se il download del file richiede troppo tempo.
per coloro che sono venuti alla ricerca di un modo basato sulla promessa in stile es6, immagino che sarebbe qualcosa del tipo:
var http = require('http');
var fs = require('fs');
function pDownload(url, dest){
var file = fs.createWriteStream(dest);
return new Promise((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));
responseSet
flag ha causato, per qualche motivo che non avevo avuto il tempo di investigare, che il mio file fosse scaricato in modo incompleto. Non sono stati rilevati errori, ma il file .txt che stavo popolando aveva la metà delle righe che dovevano essere lì. Rimozione della logica per il flag risolto. Volevo solo far notare che qualcuno aveva i problemi con l'approccio. Ancora, +1
Il codice di Vince Yuan è fantastico ma sembra essere qualcosa di sbagliato.
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);
});
});
}
Preferisco request () perché puoi usare sia http che https con esso.
request('http://i3.ytimg.com/vi/J---aiyznGQ/mqdefault.jpg')
.pipe(fs.createWriteStream('cat.jpg'))
"As of Feb 11th 2020, request is fully deprecated. No new changes are expected to land. In fact, none have landed for some time."
const download = (url, path) => new Promise((resolve, reject) => {
http.get(url, response => {
const statusCode = response.statusCode;
if (statusCode !== 200) {
return reject('Download error!');
}
const writeStream = fs.createWriteStream(path);
response.pipe(writeStream);
writeStream.on('error', () => reject('Error writing to file!'));
writeStream.on('finish', () => writeStream.close(resolve));
});}).catch(err => console.error(err));
Ciao , Penso che puoi usare il modulo child_process e il comando curl.
const cp = require('child_process');
let download = async function(uri, filename){
let command = `curl -o ${filename} '${uri}'`;
let result = cp.execSync(command);
};
async function test() {
await download('http://zhangwenning.top/20181221001417.png', './20181221001417.png')
}
test()
Inoltre , quando vuoi scaricare grandi 、 più file , puoi usare il modulo cluster per usare più core cpu.
Puoi usare https://github.com/douzi8/ajax-request#download
request.download('http://res.m.ctrip.com/html5/Content/images/57.png',
function(err, res, body) {}
);
ajax-request
non sia una libreria di terze parti?
Scarica usando promise, che risolve un flusso leggibile. mettere una logica extra per gestire il reindirizzamento.
var http = require('http');
var promise = require('bluebird');
var url = require('url');
var fs = require('fs');
var assert = require('assert');
function download(option) {
assert(option);
if (typeof option == 'string') {
option = url.parse(option);
}
return new promise(function(resolve, reject) {
var req = http.request(option, function(res) {
if (res.statusCode == 200) {
resolve(res);
} else {
if (res.statusCode === 301 && res.headers.location) {
resolve(download(res.headers.location));
} else {
reject(res.statusCode);
}
}
})
.on('error', function(e) {
reject(e);
})
.end();
});
}
download('http://localhost:8080/redirect')
.then(function(stream) {
try {
var writeStream = fs.createWriteStream('holyhigh.jpg');
stream.pipe(writeStream);
} catch(e) {
console.error(e);
}
});
Se si utilizza express, utilizzare il metodo res.download (). altrimenti uso del modulo fs.
app.get('/read-android', function(req, res) {
var file = "/home/sony/Documents/docs/Android.apk";
res.download(file)
});
(o)
function readApp(req,res) {
var file = req.fileName,
filePath = "/home/sony/Documents/docs/";
fs.exists(filePath, function(exists){
if (exists) {
res.writeHead(200, {
"Content-Type": "application/octet-stream",
"Content-Disposition" : "attachment; filename=" + file});
fs.createReadStream(filePath + file).pipe(res);
} else {
res.writeHead(400, {"Content-Type": "text/plain"});
res.end("ERROR File does NOT Exists.ipa");
}
});
}
O Quindi, se si utilizza la pipeline , chiuderebbe tutti gli altri flussi e si assicurerebbe che non vi siano perdite di memoria.
Esempio funzionante:
const http = require('http'); const { pipeline } = require('stream'); const fs = require('fs'); const file = fs.createWriteStream('./file.jpg'); http.get('http://via.placeholder.com/150/92c952', response => { pipeline( response, file, err => { if (err) console.error('Pipeline failed.', err); else console.log('Pipeline succeeded.'); } ); });
Dalla mia risposta a "Qual è la differenza tra .pipe e .pipeline su stream" .
Percorso: tipo img: jpg random uniqid
function resim(url) {
var http = require("http");
var fs = require("fs");
var sayi = Math.floor(Math.random()*10000000000);
var uzanti = ".jpg";
var file = fs.createWriteStream("img/"+sayi+uzanti);
var request = http.get(url, function(response) {
response.pipe(file);
});
return sayi+uzanti;
}
Senza biblioteca potrebbe essere difettoso solo per sottolineare. Eccone alcuni:
Protocol "https:" not supported.
Ecco il mio suggerimento:
wget
ocurl
var wget = require('node-wget-promise');
wget('http://nodejs.org/images/logo.svg');
function download(url, dest, cb) {
var request = http.get(url, function (response) {
const settings = {
flags: 'w',
encoding: 'utf8',
fd: null,
mode: 0o666,
autoClose: true
};
// response.pipe(fs.createWriteStream(dest, settings));
var file = fs.createWriteStream(dest, settings);
response.pipe(file);
file.on('finish', function () {
let okMsg = {
text: `File downloaded successfully`
}
cb(okMsg);
file.end();
});
}).on('error', function (err) { // Handle errors
fs.unlink(dest); // Delete the file async. (But we don't check the result)
let errorMsg = {
text: `Error in file downloadin: ${err.message}`
}
if (cb) cb(errorMsg);
});
};
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.cryptocompare.com/media/19684/doge.png', 'icons/taskks12.png', function(){
console.log('done');
});
Ecco ancora un altro modo per gestirlo senza dipendenze di terze parti e anche cercare reindirizzamenti:
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.
});
});
}
download.js (es. /project/utils/download.js)
const fs = require('fs');
const request = require('request');
const download = (uri, filename, callback) => {
request.head(uri, (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);
});
};
module.exports = { download };
app.js
...
// part of imports
const { download } = require('./utils/download');
...
// add this function wherever
download('https://imageurl.com', 'imagename.jpg', () => {
console.log('done')
});
Possiamo utilizzare il modulo del nodo di download ed è molto semplice, fare riferimento di seguito https://www.npmjs.com/package/download
var requestModule=require("request");
requestModule(filePath).pipe(fs.createWriteStream('abc.zip'));