Dav*_*ave 6 node.js meteor meteorite
我需要允许我的应用程序的用户使用Meteor下载文件.目前我所做的是当用户请求将我输入的文件下载到Mongo中的"fileRequests"集合时,该文档包含文件位置和请求的时间戳,并返回新创建的请求的ID.当客户端获得新ID时,它会立即转到mydomain.com/uploads/:id.然后我在Meteor之前使用这样的东西来拦截请求:
var connect = Npm.require("connect");
var Fiber = Npm.require("fibers");
var path = Npm.require('path');
var fs = Npm.require("fs");
var mime = Npm.require("mime");
__meteor_bootstrap__.app
.use(connect.query())
.use(connect.bodyParser()) //I add this for file-uploading
.use(function (req, res, next) {
Fiber(function() {
if(req.method == "GET") {
// get the id here, and stream the file using fs.createReadStream();
}
next();
}).run();
});
Run Code Online (Sandbox Code Playgroud)
我检查确保文件请求是在不到5秒之前完成的,我在查询后立即删除了请求文档.
这是有效的,并且我认为是安全的(足够).没有人登录就没有人可以提出请求.5秒是一个非常小的窗口,有人能够劫持创建的请求URL,但我觉得我的解决方案感觉不对.感觉很脏!
所以我试图使用Meteor-Router来完成同样的事情.通过这种方式,我可以检查他们是否正确登录而没有对世界诡计开放5秒.
所以这是我为此写的代码:
Meteor.Router.add('/uploads/:id', function(id) {
var path = Npm.require('path');
var fs = Npm.require("fs");
var mime = Npm.require("mime");
var res = this.response;
var file = FileSystem.findOne({ _id: id });
if(typeof file !== "undefined") {
var filename = path.basename(file.filePath);
var filePath = '/var/MeteorDMS/uploads/' + filename;
var stat = fs.statSync(filePath);
res.setHeader('Content-Disposition', 'attachment; filename=' + filename);
res.setHeader('Content-Type', mime.lookup(filePath));
res.setHeader('Content-Length', stat.size);
var filestream = fs.createReadStream(filePath);
filestream.pipe(res);
return;
}
});
Run Code Online (Sandbox Code Playgroud)
这看起来很棒,适合其他代码并且易于阅读,不涉及黑客,但是!它不起作用!浏览器旋转和旋转,从来不知道该怎么做.我有ZERO错误消息.我可以继续在其他标签上使用该应用.我不知道它在做什么,它永远不会停止"加载".如果我重新启动服务器,我得到一个包含所有正确标头的0字节文件,但我没有得到数据.
任何帮助是极大的赞赏!!
编辑:
在进一步挖掘之后,我注意到尝试将响应对象转换为JSON对象会导致循环结构错误.
现在有趣的是,当我收听"data"事件的文件流,并尝试对响应对象进行字符串化时,我没有得到该错误.但是,如果我尝试在我的第一个解决方案中做同样的事情(听"数据"并将响应字符串化),我再次得到错误.
因此,使用Meteor-Router解决方案会对响应对象产生一些影响.我还注意到,在"data"事件中,response.finished被标记为true.
filestream.on('data', function(data) {
fs.writeFile('/var/MeteorDMS/afterData', JSON.stringify(res));
});
Run Code Online (Sandbox Code Playgroud)
Meteor 路由器安装了一个中间件来进行路由。所有 Connect 中间件要么必须调用next()(恰好一次)以指示响应尚未解决,要么必须通过调用res.end()或通过管道传递到响应来解决响应。不允许两者都做。
我研究了中间件的源代码(见下文)。我们看到我们可以返回false告诉中间件调用next()。这意味着我们声明这条路由没有解决响应问题,我们想让其他中间件完成他们的工作。
或者我们可以返回一个模板名称、一个文本、一个数组[status, text]或一个数组[status, headers, text],中间件将通过res.end()使用我们返回的数据进行调用来代表我们解决响应问题。
然而,通过管道响应,我们已经解决了响应。Meteor 路由器不应调用next()nor res.end()。
我们通过分叉 Meteor 路由器并进行一些小改动解决了这个问题。我们将第else87 行(之后if (output === false))替换为:
else if (typeof(output)!="undefined") {
Run Code Online (Sandbox Code Playgroud)
请参阅我的 fork 中带有 sha 8d8fc23d9c的提交。
这种方式return;在route方法中会告诉路由器什么也不做。当然,您已经通过管道解决了响应问题。
中间件的源代码如 sha f910a090ae提交中所示:
// hook up the serving
__meteor_bootstrap__.app
.use(connect.query()) // <- XXX: we can probably assume accounts did this
.use(this._config.requestParser(this._config.bodyParser))
.use(function(req, res, next) {
// need to wrap in a fiber in case they do something async
// (e.g. in the database)
if(typeof(Fiber)=="undefined") Fiber = Npm.require('fibers');
Fiber(function() {
var output = Meteor.Router.match(req, res);
if (output === false) {
return next();
} else {
// parse out the various type of response we can have
// array can be
// [content], [status, content], [status, headers, content]
if (_.isArray(output)) {
// copy the array so we aren't actually modifying it!
output = output.slice(0);
if (output.length === 3) {
var headers = output.splice(1, 1)[0];
_.each(headers, function(value, key) {
res.setHeader(key, value);
});
}
if (output.length === 2) {
res.statusCode = output.shift();
}
output = output[0];
}
if (_.isNumber(output)) {
res.statusCode = output;
output = '';
}
return res.end(output);
}
}).run();
});
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1508 次 |
| 最近记录: |