Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

generating and serving static files with Meteor

Tags:

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?

like image 907
bento Avatar asked Nov 02 '12 19:11

bento


People also ask

How static files are served in node?

In your node application, you can use node-static module to serve static resources. The node-static module is an HTTP static-file server module with built-in caching. First of all, install node-static module using NPM as below. After installing node-static module, you can create static file server in Node.


2 Answers

In version 0.6.6.3 0.7.x - 1.3.x you can do the following:

To write

var fs = Npm.require('fs');
var filePath = process.env.PWD + '/.uploads_dir_on_server/' + fileName;
fs.writeFileSync(filePath, data, 'binary');

To serve

In vanilla meteor app

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

When using iron:router

This should be a server side route (ex: defined in a file in /server/ folder)

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

Outdated format:

Router.map(function() {
    this.route('serverFile', {
        ...// same as object above
    }
});

Notes

  • process.env.PWD will give you the project root
  • if you plan to put files inside your project

    • don't use the public or private meteor folders
    • use dot folders (eg. hidden folders ex: .uploads)

    Not respecting these two will cause local meteor to restart on every upload, unless you run your meteor app with: meteor run --production

  • I've used this approach for a simple image upload & serve (based on dario's version)
  • Should you wish for more complex file management please consider CollectionFS
like image 192
Matyas Avatar answered Oct 05 '22 07:10

Matyas


The symlink hack will no longer work in Meteor (from 0.6.5). Instead I suggest creating a package with similar code to the following:

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']));
like image 28
Jacott Avatar answered Oct 05 '22 05:10

Jacott