gpt4 book ai didi

node.js - 如何在 Meteor 路由器(NodeJS)中使用文件系统的 createReadStream

转载 作者:搜寻专家 更新时间:2023-10-31 22:35:05 24 4
gpt4 key购买 nike

我需要允许我的应用程序的用户使用 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;
}
});

这看起来很棒,与代码的其余部分完全吻合并且易于阅读,不涉及黑客攻击,但是!它不起作用!浏览器不停地旋转,永远不知道该做什么。我收到零错误消息。我可以继续在其他选项卡上使用该应用程序。我不知道它在做什么,它永远不会停止“加载”。如果我重新启动服务器,我会得到一个包含所有正确 header 的 0 字节文件,但我没有得到数据。

非常感谢任何帮助!!

编辑:

在深入研究之后,我注意到尝试将响应对象转换为 JSON 对象会导致循环结构错误。

有趣的是,当我监听“数据”事件的文件流,并尝试将响应对象字符串化时,我没有收到该错误。但是,如果我尝试在我的第一个解决方案中做同样的事情(听“数据”并将响应字符串化),我会再次收到错误。

所以使用 Meteor-Router 解决方案,响应对象发生了一些变化。我还注意到在“数据”事件上,response.finished 被标记为 true。

filestream.on('data', function(data) {
fs.writeFile('/var/MeteorDMS/afterData', JSON.stringify(res));
});

最佳答案

Meteor 路由器安装了一个中间件来进行路由。所有 Connect 中间件要么必须调用 next()(恰好一次)以指示响应尚未确定,要么必须通过调用 res.end() 或通过管道响应。不允许两者都做。

我研究了中间件的源代码(见下文)。我们看到我们可以返回false来告诉中间件调用next()。这意味着我们声明这条路由没有解决响应,我们想让其他中间件完成它们的工作。

或者我们可以返回模板名称、文本、数组[status, text] 或数组[status, headers, text],中间件将使用我们返回的数据调用 res.end() 代表我们解决响应。

但是,通过管道响应,我们已经确定了响应。 Meteor 路由器不应调用 next()res.end()

我们通过 fork Meteor 路由器并进行小改动解决了这个问题。我们将第 87 行中的 else(在 if (output === false) 之后)替换为:

else if (typeof(output)!="undefined") {

查看带有 sha 8d8fc23d9c 的提交在我的 fork 里。

这样,路由方法中的return; 将告诉路由器什么都不做。当然,您已经通过管道处理响应了。


中间件的源代码与 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();
});

关于node.js - 如何在 Meteor 路由器(NodeJS)中使用文件系统的 createReadStream,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17073431/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com