Como faço para baixar um arquivo com Node.js sem usar bibliotecas de terceiros ?
Eu não preciso de nada de especial. Eu só quero fazer o download de um arquivo de um determinado URL e salvá-lo em um determinado diretório.
Como faço para baixar um arquivo com Node.js sem usar bibliotecas de terceiros ?
Eu não preciso de nada de especial. Eu só quero fazer o download de um arquivo de um determinado URL e salvá-lo em um determinado diretório.
Respostas:
Você pode criar uma GET
solicitação HTTP e canalizá-la response
para um fluxo de arquivos gravável:
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 você deseja oferecer suporte à coleta de informações na linha de comando - como especificar um arquivo ou diretório de destino ou URL - consulte algo como o 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
linha; talvez http://i3.ytimg.com/vi/J---aiyznGQ/mqdefault.jpg
(e substitua file.png
por file.jpg
).
https
deverá usar, https
caso contrário, ocorrerá erro.
Não se esqueça de lidar com erros! O código a seguir é baseado na resposta 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);
});
};
download()
em si pipe
capaz?
Como Michelle Tilley disse, mas com o fluxo de controle apropriado:
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);
});
});
}
Sem aguardar o finish
evento, scripts ingênuos podem acabar com um arquivo incompleto.
Edit: Obrigado a @Augusto Roman por apontar que cb
deve ser passado para file.close
, e não chamado explicitamente.
download()
, como faria? O que eu colocaria como cb
argumento? Tenho a download('someURI', '/some/destination', cb)
, mas não entendo o que colocar no cb
Falando em lidar com erros, é ainda melhor ouvir solicitações de erros também. Eu até validaria verificando o código de resposta. Aqui é considerado sucesso apenas para 200 códigos de resposta, mas outros códigos podem ser 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 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);
});
};
Apesar da relativa simplicidade desse código, aconselho o uso do módulo request, pois ele lida com muitos outros protocolos (hello HTTPS!) Que não são suportados nativamente por http
.
Isso seria feito assim:
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
o cb on finish
nunca for chamado.
A resposta de gfxmonk tem uma corrida de dados muito estreita entre o retorno de chamada e a file.close()
conclusão. file.close()
na verdade, recebe um retorno de chamada chamado quando o fechamento é concluído. Caso contrário, o uso imediato do arquivo poderá falhar (muito raramente!).
Uma solução 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.
});
});
}
Sem aguardar o evento de conclusão, os scripts ingênuos podem acabar com um arquivo incompleto. Sem agendar o cb
retorno de chamada por meio do fechamento, você pode ter uma corrida entre acessar o arquivo e o arquivo realmente estar pronto.
var request =
for removida?
Talvez o node.js tenha mudado, mas parece que existem alguns problemas com as outras soluções (usando o nó v8.1.2):
file.close()
no finish
evento. Por padrão, fs.createWriteStream
é definido como autoClose: https://nodejs.org/api/fs.html#fs_fs_createwritestream_path_optionsfile.close()
deve ser chamado por erro. Talvez isso não seja necessário quando o arquivo for excluído ( unlink()
), mas normalmente é: https://nodejs.org/api/stream.html#stream_readable_pipe_destination_optionsstatusCode !== 200
fs.unlink()
sem retorno de chamada é preterido (gera aviso)dest
arquivo existir; é substituídoAbaixo está uma solução modificada (usando ES6 e promessas) que lida com esses problemas.
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");
porconst http = require("http");
O código a seguir é baseado na resposta de 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();
});
});
Não crie arquivo quando ocorrer um erro e prefira usar o tempo limite para fechar sua solicitação após X segundos.
http.get("http://example.com/yourfile.html",function(){})
http.get
. O vazamento de memória é apenas se o arquivo demorar muito para ser baixado.
para aqueles que vieram em busca da maneira baseada em promessas no estilo es6, acho que seria algo como:
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
A flag causou, por algum motivo que eu não tive tempo de investigar, meu arquivo para ser baixado incompletamente. Nenhum erro foi exibido, mas o arquivo .txt que eu estava preenchendo tinha metade das linhas que precisavam estar lá. A remoção da lógica do sinalizador a corrigiu. Só queria salientar se alguém tinha problemas com a abordagem. Ainda assim, +1
O código de Vince Yuan é ótimo, mas parece haver algo errado.
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);
});
});
}
Prefiro request () porque você pode usar http e https com ele.
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));
Oi. Eu acho que você pode usar o módulo child_process e o 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()
Além disso, quando você deseja baixar grandes arquivos múltiplos, você pode usar o módulo de cluster para usar mais núcleos de CPU.
Você pode usar 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
não é uma biblioteca de terceiros?
Faça o download usando a promessa, que resolve um fluxo legível. coloque lógica extra para lidar com o redirecionamento.
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 você estiver usando express use o método res.download (). caso contrário, use o módulo fs.
app.get('/read-android', function(req, res) {
var file = "/home/sony/Documents/docs/Android.apk";
res.download(file)
});
(ou)
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");
}
});
}
OEntão, se você usar o pipeline , ele fechará todos os outros fluxos e garantirá que não haja vazamentos de memória.
Exemplo de trabalho:
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.'); } ); });
Da minha resposta para "Qual é a diferença entre .pipe e .pipeline em fluxos" .
Caminho: img tipo: jpg uniqid aleatório
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;
}
Sem biblioteca, poderia ser um buggy apenas para apontar. Aqui estão alguns:
Protocol "https:" not supported.
Aqui está minha sugestão:
wget
oucurl
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');
});
Aqui está outra maneira de lidar com isso sem dependência de terceiros e também procurando por redirecionamentos:
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 (por exemplo, /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')
});
Podemos usar o módulo do nó de download e é muito simples, consulte abaixo https://www.npmjs.com/package/download
var requestModule=require("request");
requestModule(filePath).pipe(fs.createWriteStream('abc.zip'));