20

I'm looking to create static text files based upon the content of a supplied object, which can then be downloaded by the user. Here's what I was planning on doing:

  1. When the user hits 'export' the application calls a Meteor.method() which, in turn, parses and writes the file to the public directory using typical Node methods.

  2. Once the file is created, in the callback from Meteor.method() I provide a link to the generated file. For example, 'public/userId/file.txt'. The user can then choose to download the file at that link.

  3. I then use Meteor's Connect modele (which it uses internally) to route any requests to the above URL to the file itself. I could do some permissions checking based on the userId and the logged in state of the user.

The problem: When static files are generated in public, the web page automatically reloads each time. I thought that it might make more sense to use something like Express to generate a REST endpoint, which could deal with creating the files. But then I'm not sure how to deal with permissions if I don't have access to the Meteor session data.

Any ideas on the best strategy here?

4

4 回答 4

24

在版本中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会给你项目根目录
  • 如果您打算将文件放入项目中

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

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

  • 我已经使用这种方法进行简单的图像上传和服务(基于dario的版本)
  • 如果您希望进行更复杂的文件管理,请考虑CollectionFS
于 2013-12-03T18:14:38.213 回答
16

符号链接 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']));
于 2013-07-27T01:38:01.977 回答
5

我遇到了完全相同的问题,与您的服务器生成的文件相比,我需要用户上传文件。我通过在同一文件夹级别上创建一个“上传”文件夹作为“客户端公共服务器”的兄弟来解决它。然后我创建了一个指向“.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”这样的文件夹,而不是我的“上传”文件夹,每次服务器启动时您都需要这样做,因为每次服务器启动时都会生成这些文件夹,例如您的实现中的文件更改。

于 2012-11-10T17:41:11.743 回答
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));
    }
});

然而,这种方法有一个很大的缺点。服务器端路由不提供获取当前登录用户的简单方法。在 GitHub 上查看此问题

于 2014-09-05T00:53:54.397 回答