我希望根据提供的对象的内容创建静态文本文件,然后由用户下载。这就是我计划做的事情:
当用户点击“导出”时,应用程序会调用Meteor.method()
,然后使用典型的Node方法将文件解析并写入公共目录。
创建文件后,在Meteor.method()
的回调中,我提供了生成文件的链接。例如,'public / userId / file.txt'。然后,用户可以选择在该链接下载文件。
然后我使用Meteor的Connect modele
(它在内部使用)将对上述URL的任何请求路由到文件本身。我可以根据userId和用户的登录状态进行一些权限检查。
问题:当公共生成静态文件时,网页每次都会自动重新加载。我认为使用像Express这样的东西生成一个REST端点更有意义,它可以处理创建文件。但是,如果我无法访问Meteor会话数据,那么我不确定如何处理权限。
有关最佳策略的任何想法吗?
答案 0 :(得分:23)
在版本 0.6.6.3 0.7.x - 1.3.x 中,您可以执行以下操作:
var fs = Npm.require('fs');
var filePath = process.env.PWD + '/.uploads_dir_on_server/' + fileName;
fs.writeFileSync(filePath, data, 'binary');
var fs = Npm.require('fs');
WebApp.connectHandlers.use(function(req, res, next) {
var re = /^\/uploads_url_prefix\/(.*)$/.exec(req.url);
if (re !== null) { // Only handle URLs that start with /uploads_url_prefix/*
var filePath = process.env.PWD + '/.uploads_dir_on_server/' + re[1];
var data = fs.readFileSync(filePath);
res.writeHead(200, {
'Content-Type': 'image'
});
res.write(data);
res.end();
} else { // Other urls will have default behaviors
next();
}
});
这应该是服务器端路由(例如:在/server/
文件夹中的文件中定义)
编辑(2016年5月9日)
var fs = Npm.require('fs');
Router.route('uploads', {
name: 'uploads',
path: /^\/uploads_url_prefix\/(.*)$/,
where: 'server',
action: function() {
var filePath = process.env.PWD + '/.uploads_dir_on_server/' + this.params[0];
var data = fs.readFileSync(filePath);
this.response.writeHead(200, {
'Content-Type': 'image'
});
this.response.write(data);
this.response.end();
}
});
过时的格式:
Router.map(function() {
this.route('serverFile', {
...// same as object above
}
});
process.env.PWD
将为您提供项目根如果您打算将文件放入项目中
public
或private
流星文件夹.uploads
)不尊重这两个会导致本地流星在每次上传时重新启动,除非您使用以下网址运行您的流星应用程序:meteor run --production
答案 1 :(得分:16)
符号链接黑客将不再在Meteor中工作(从0.6.5开始)。相反,我建议创建一个包含类似代码的包:
packge.js
Package.describe({
summary: "Application file server."
});
Npm.depends({
connect: "2.7.10"
});
Package.on_use(function(api) {
api.use(['webapp', 'routepolicy'], 'server');
api.add_files([
'app-file-server.js',
], 'server');
});
app-file-server.js
var connect = Npm.require('connect');
RoutePolicy.declare('/my-uploaded-content', 'network');
// Listen to incoming http requests
WebApp.connectHandlers
.use('/my-uploaded-content', connect.static(process.env['APP_DYN_CONTENT_DIR']));
答案 2 :(得分:5)
我遇到了完全相同的问题,我需要用户上传文件而不是服务器生成的文件。我通过创建一个“uploads”文件夹作为同一文件夹级别的“客户端公共服务器”的兄弟来解决它。然后我创建了一个到'.meteor / local / build / static'文件夹的类似链接,如
ln -s ../../../../uploads .meteor/local/build/static/
但在服务器启动时使用nodejs filesystem api
Meteor.startup(function () {
var fs = Npm.require('fs');
fs.symlinkSync('../../../../uploads', '.meteor/local/build/static/uploads');
};
在您的情况下,您可能有一个像“generatedFiles”这样的文件夹而不是我的“uploads”文件夹 每次服务器启动时都需要这样做,因为每次服务器启动时都会生成这些文件夹,例如:文件在实施中发生变化。
答案 3 :(得分:1)
另一种选择是使用服务器端路由生成内容并将其发送到用户的浏览器以供下载。例如,以下内容将按ID查找用户并将其作为JSON返回。系统将提示最终用户将响应保存到具有Content-Disposition标头中指定名称的文件。其他标题(例如Expires)也可以添加到响应中。如果用户不存在,则返回404。
Router.route("userJson", {
where: "server",
path: "/user-json/:userId",
action: function() {
var user = Meteor.users.findOne({ _id: this.params.userId });
if (!user) {
this.response.writeHead(404);
this.response.end("User not found");
return;
}
this.response.writeHead(200, {
"Content-Type": "application/json",
"Content-Disposition": "attachment; filename=user-" + user._id + ".json"
});
this.response.end(JSON.stringify(user));
}
});
然而,这种方法有一个很大的缺点。服务器端路由不提供获取当前登录用户的简便方法。请参阅this issue on GitHub。