【问题标题】:generating and serving static files with Meteor使用 Meteor 生成和提供静态文件
【发布时间】:2012-10-23 11:53:15
【问题描述】:

我希望根据提供的对象的内容创建静态文本文件,然后用户可以下载这些文件。这是我打算做的事情:

  1. 当用户点击“导出”时,应用程序会调用Meteor.method(),然后使用典型的 Node 方法解析文件并将其写入公共目录。

  2. 创建文件后,在来自Meteor.method() 的回调中,我提供了指向生成文件的链接。例如,“public/userId/file.txt”。然后,用户可以选择在该链接下载文件。

  3. 然后我使用 Meteor 的 Connect modele(它在内部使用)将任何对上述 URL 的请求路由到文件本身。我可以根据 userId 和用户的登录状态做一些权限检查。

问题:当公开生成静态文件时,网页每次都会自动重新加载。我认为使用 Express 之类的东西来生成 REST 端点可能更有意义,它可以处理创建文件。但是如果我无权访问 Meteor 会话数据,我不确定如何处理权限。

关于这里的最佳策略有什么想法吗?

【问题讨论】:

  • 我认为有一种方法可以停止观察某些目录......当该特定目录中的内容发生变化时,这将阻止流星更新。
  • 谢谢牧师。我决定采用客户端技术来创建和保存文件。 window.saveAs()(和其他变通方法)可用于创建文件。

标签: javascript express meteor


【解决方案1】:

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();
    }
});

使用 Iron:router 时

这应该是服务器端路由(例如:在/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 会给你项目根目录
  • 如果您打算将文件放入项目中

    • 不要使用 publicprivate meteor 文件夹
    • 使用点文件夹(例如隐藏文件夹,例如:.uploads

    不尊重这两个将导致本地流星在每次上传时重新启动,除非你运行你的流星应用程序:meteor run --production

  • 我使用这种方法进行了简单的图片上传和服务(基于dario 的版本)
  • 如果您希望进行更复杂的文件管理,请考虑CollectionFS

【讨论】:

  • 所以如果我有很多用户都有自己的文件,我会遇到这个目录结构的问题吗? \Users\user_name\created_file.txt
  • 我希望用户在我创建 pdf 后能够 d/l,但不能从该目录中 d/l 任何文件。此外,我不希望任何其他人能够 d/l 除了正确的用户之外的文件。基本上,我不会让每个人都可以访问文件,只有用户可以访问,而只是该用户的文件。
  • 这是您必须自己验证/实施的内容。基本上您必须: 1. 确定发起请求的用户。 2. 检查他对所请求资源的权限。 3. 如果所有信息都ok,则发送它,否则返回HTTP 403 Forbidden。 (这就是我解决问题的方式)
  • @Matyas,谢谢。我无法获取我的 server/.files/users/some_user_id/test.pdf 中的这个 pdf 来呈现。当我转到 localhost:3000/serve-file/.files/users/some_user_id/test.pdf 时,操作函数永远不会执行。我正在使用最新的 IR。 Router.route('serve-file', {where: 'server', path: '不明白这个', action: function () { var filePath=process.env.PWD + '/.files/users/' + this.userId + '/' + this.params[1]; var data = fs.readFileSync(filePath); this.response.writeHead(200, { 'Content-Type': 'image'}); this.response .write(data); this.response.end();
  • @Aaron 因为您提供的路径 ('don't understand this') 不是有效的路径模式。您基本上没有将处理程序映射到路径
【解决方案2】:

符号链接 hack 将不再适用于 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']));

【讨论】:

  • 到目前为止,这应该是正确的答案。您还可以添加 /my-uploaded-content 是 url 路径,process.env['APP_DYN_CONTENT_DIR'] 是根路径目录
  • 如何使用此解决方案管理缓存?我想要无限缓存 + 在图像名称中添加哈希。
【解决方案3】:

我遇到了完全相同的问题,与您的服务器生成的文件相比,我需要用户上传文件。我通过在同一文件夹级别上创建一个“上传”文件夹作为“客户端公共服务器”的兄弟来解决它。然后我创建了一个指向“.meteor/local/build/static”文件夹的符号链接,例如

ln -s ../../../../uploads .meteor/local/build/static/ 

但在服务器启动时使用 nodejs 文件系统 api

Meteor.startup(function () {
    var fs = Npm.require('fs');

    fs.symlinkSync('../../../../uploads', '.meteor/local/build/static/uploads'); 
};

在您的情况下,您可能有一个像“generatedFiles”这样的文件夹,而不是我的“上传”文件夹 每次服务器启动时都需要这样做,因为每次服务器启动时都会生成这些文件夹,例如您的实现中的文件更改。

【讨论】:

    【解决方案4】:

    另一种选择是使用服务器端路由来生成内容并将其发送到用户的浏览器以供下载。例如,以下将按 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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-04-01
      • 1970-01-01
      • 2020-01-29
      • 2010-10-18
      • 2014-11-07
      • 2019-07-16
      • 1970-01-01
      相关资源
      最近更新 更多