如何使用Node.js下载文件(不使用第三方库)?

如何在不使用第三方库的情况下使用 Node.js下载文件?

我不需要任何特别的东西。 我只想从给定的URL下载文件,然后将其保存到给定的目录。

您可以创build一个HTTP GET请求,并将其response转换为可写的文件stream:

 var http = require('http'); var fs = require('fs'); var file = fs.createWriteStream("file.jpg"); var request = http.get("http://i3.ytimg.com/vi/J---aiyznGQ/mqdefault.jpg", function(response) { response.pipe(file); }); 

如果你想支持在命令行上收集信息 – 比如指定一个目标文件或者目录,或者URL,那么可以看看Commander这样的东西。

不要忘记处理错误! 以下代码基于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); }); }; 

正如布兰登·蒂利(Brandon Tilley)所说的那样,

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

没有等待finish事件,天真的脚本可能会以不完整的文件结束。

编辑:感谢@Augusto罗马指出, cb应传递给file.close ,不明确调用。

说到处理错误,它甚至更好地倾听请求错误。 我甚至通过检查响应代码来validation。 在这里它只被认为是成功的200个响应代码,但其他代码可能是好的。

 var fs = require('fs'); var http = require('http'); var download = function(url, dest, cb) { var file = fs.createWriteStream(dest); var request = http.get(url, function(response) { // check if response is success if (response.statusCode !== 200) { return cb('Response status was ' + response.statusCode); } response.pipe(file); file.on('finish', function() { file.close(cb); // close() is async, call cb after close completes. }); }); // check for request error too request.on('error', function (err) { fs.unlink(dest); return cb(err.message); }); file.on('error', function(err) { // Handle errors fs.unlink(dest); // Delete the file async. (But we don't check the result) return cb(err.message); }); }; 

尽pipe这段代码相对简单,但我build议使用请求模块,因为它处理更多协议(hello HTTPS!),这些协议本来不受http支持。

这样做可以这样做:

 var fs = require('fs'); var request = require('request'); var download = function(url, dest, cb) { var file = fs.createWriteStream(dest); var sendReq = request.get(url); // verify response code sendReq.on('response', function(response) { if (response.statusCode !== 200) { return cb('Response status was ' + response.statusCode); } }); // check for request errors sendReq.on('error', function (err) { fs.unlink(dest); return cb(err.message); }); sendReq.pipe(file); file.on('finish', function() { file.close(cb); // close() is async, call cb after close completes. }); file.on('error', function(err) { // Handle errors fs.unlink(dest); // Delete the file async. (But we don't check the result) return cb(err.message); }); }; 

gfxmonk的答案在callback和file.close()完成之间有一个非常紧密的数据竞争。 file.close()实际上是在closures完成时调用的一个callback函数。 否则,立即使用该文件可能会失败(很less!)。

完整的解决scheme是:

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

无需等待结束事件,天真的脚本可能会以不完整的文件结束。 如果没有通过closures调度cbcallback,您可能会在访问文件和实际准备好的文件之间进行竞争。

超时解决scheme,防止内存泄漏:

以下代码是基于Brandon Tilley的回答:

 var http = require('http'), fs = require('fs'); var request = http.get("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(); }); }); 

发生错误时不要生成文件,并在X secondes之后使用超时来closures请求。

对于那些以es6风格的诺言为基础的人来说,我想这应该是这样的:

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

你可以使用https://github.com/douzi8/ajax-request#download

 request.download('http://res.m.ctrip.com/html5/Contenthttp://img.dovov.com57.png', function(err, res, body) {} ); 

Vince Yuan的代码很棒,但是似乎是错的。

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

path:imgtypes:jpg随机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; } 
 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)); 
 app.get('/read-android', function(req, res) { var file = "/home/sony/Documents/docs/Android.apk"; res.download(file) }); 

(要么)

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

使用promise下载,parsing可读的stream。 把额外的逻辑来处理redirect。

 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); } }); 
 var requestModule=require("request"); requestModule(filePath).pipe(fs.createWriteStream('abc.zip')); 

也许node.js已经改变,但似乎有其他解决scheme(使用节点v8.1.2)的一些问题:

  1. finish事件中不需要调用file.close() 。 默认情况下, fs.createWriteStream被设置为autoClose: https ://nodejs.org/api/fs.html#fs_fs_createwritestream_path_options
  2. 应该在错误时调用file.close() 。 当文件被删除( unlink() )时可能不需要,但通常是: https : //nodejs.org/api/stream.html#stream_readable_pipe_destination_options
  3. statusCode !== 200上的临时文件不会被删除
  4. fs.unlink()没有callback被弃用(输出警告)
  5. 如果dest文件存在; 它被覆盖

下面是一个修改的解决scheme(使用ES6和promise)来处理这些问题。

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

我们可以使用下载节点模块,它非常简单,请参阅下面的https://www.npmjs.com/package/download

下载文本文件使用这个

 downloadText("some text"); 

在下面下载任何文件使用

 var path = '../bin/filename.fileExtention'; res.download(path);