我需要允许我的应用程序的用户使用 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();
});
我检查以确保文件请求是在不到 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;
}
});
这看起来很棒,与其余代码完全吻合,并且易于阅读,不涉及黑客攻击,但是!它不起作用!浏览器旋转和旋转,永远不知道该做什么。我有零错误消息出现。我可以继续在其他选项卡上使用该应用程序。我不知道它在做什么,它永远不会停止“加载”。如果我重新启动服务器,我会得到一个包含所有正确标题的 0 字节文件,但我没有得到数据。
任何帮助是极大的赞赏!!
编辑:
在深入研究之后,我注意到尝试将响应对象转换为 JSON 对象会导致循环结构错误。
现在有趣的是,当我监听“数据”事件的文件流并尝试对响应对象进行字符串化时,我没有收到该错误。但是,如果我尝试在我的第一个解决方案中做同样的事情(听“数据”并将响应字符串化),我会再次收到错误。
因此,使用 Meteor-Router 解决方案时,响应对象会发生一些事情。我还注意到“数据”事件 response.finished 被标记为 true。
filestream.on('data', function(data) {
fs.writeFile('/var/MeteorDMS/afterData', JSON.stringify(res));
});