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?
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();
}
});
process.env.PWD
will give you the project rootif you plan to put files inside your project
public
or private
meteor folders.uploads
)Not respecting these two will cause local meteor to restart on every upload, unless you run your meteor app with: meteor run --production
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();
},
});
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With