如何使用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)的一些问题:
-
finish
事件中不需要调用file.close()
。 默认情况下,fs.createWriteStream
被设置为autoClose: https ://nodejs.org/api/fs.html#fs_fs_createwritestream_path_options - 应该在错误时调用
file.close()
。 当文件被删除(unlink()
)时可能不需要,但通常是: https : //nodejs.org/api/stream.html#stream_readable_pipe_destination_options -
statusCode !== 200
上的临时文件不会被删除 -
fs.unlink()
没有callback被弃用(输出警告) - 如果
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);