Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically insert files into meteor public folder without hiding it

Tags:

meteor

I have a meteor application that generates images. After they are generated, I want to serve them. But each time I write to the public folder, my meteor server restarts. I searched for a solution and found several workarounds:

  • Serve files outside of the project folder - At the moment I don't know how to achieve this, would I have to write some kind of middleware that integrates into meteor?

  • Add a tilde ~ to the folder in public/ - which seems to make meteor ignore the folder altogether, when trying to access files in the folder I get redirected to my root page.

  • Run meteor in production mode. Seems like a dirty workaround for me. Right now, meteor run --production still restarts my server so I have to bundle my app, reinstall fibers every time, set my environment variables and then run the app. Every time I change something.

Are there any other solutions out there?

like image 476
raffomania Avatar asked Jul 19 '13 07:07

raffomania


3 Answers

The accepted answer did not work for me, but as of version 0.6.6.3 you can do the following:

var fs = Npm.require('fs');
WebApp.connectHandlers.use(function(req, res, next) {
    var re = /^\/url_path\/(.*)$/.exec(req.url);
    if (re !== null) {   // Only handle URLs that start with /url_path/*
        var filePath = process.env.PWD + '/.server_path/' + re[1];
        var data = fs.readFileSync(filePath, data);
        res.writeHead(200, {
                'Content-Type': 'image'
            });
        res.write(data);
        res.end();
    } else {  // Other urls will have default behaviors
        next();
    }
});

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

like image 177
Matyas Avatar answered Oct 17 '22 06:10

Matyas


It's not that easy.

  • Writing to public is out of question, as Meteor manages this folder and thus restarts itself on every file change.

  • Writing to ignored folder (starting with . or ending with ~, or even outside of Meteor directory) is an option. However, you'll need to manually serve those files. A small middleware would do the trick:

 

__meteor_bootstrap__.app.stack.splice (0, 0, {
  route: '/mediaPathOfChoice',
  handle: function(req, res, next) {

    /* Read the proper file based on req.url */

    res.writeHead(200, {
      'Content-Type': /* Put the item MIME type here */
    });
    res.write(/* Put item contents here */);
    res.end();

  },

});

 

  • For many publishing options, writing files on the application server is not the best solution anyway. Consider setting up a media server for your files - S3 buckets are solid and cheap for this purpose.
like image 7
Hubert OG Avatar answered Oct 17 '22 06:10

Hubert OG


So I solved this with iron:router. I made a folder, assets, that's outside the meteor folder. I then have code like this to serve the files

var fs = Npm.require('fs');
var path = Npm.require('path');

Router.map(function () {
  this.route('assets', {
    where: 'server',
    path: '/assets/:filename(.*)',
    action: function() {
      var basePath = process.env.ASSET_PATH;
      var filename = path.normalize(path.join(basePath, this.params.filename));
      var res = this.response;
      if (filename.substr(0, basePath.length) != basePath ||
          !fs.existsSync(filename) ||
          !fs.statSync(filename).isFile()) {
        res.writeHead(404, {'Content-Type': 'text/html'});
        res.end('404: no such asset: ' + this.params.filename);
        return;
      }
      var data = fs.readFileSync(filename);
      var mimeType = mime.lookup(filename);
      res.writeHead(200, { 'Content-Type': mimeType });
      res.write(data);
      res.end();
    },
  });
});

Mime lookup looks like this

var mime = {
  lookup: (function() {

    var mimeTypes = {
      ".html": "text/html",
      ".js":   "application/javascript",
      ".json": "application/json",
      ".png":  "image/png",
      ".gif":  "image/gif",
      ".jpg":  "image/jpg",
    };

    return function(name) {
      var type = mimeTypes[path.extname(name)];
      return type || "text/html";
    };
  }()),
};

That code is only executed on the server so put it in the server folder or make sure it's in an if (Meteor.isServer) check.

As you can see above I'm using an environment variable for the path to the asset folder. So I can run meteor like this

ASSET_PATH=/some/path/to/assets meteor

I'm also using the meteorhacks:npm module for the fs and path modules.

like image 3
gman Avatar answered Oct 17 '22 05:10

gman