How to serve static content (images, fonts etc.) using iron router
Asked Answered
E

3

29

I just started working with iron router on meteor. I need to show an image on homepage. I was able to configure route for 'home' using the client side routing. For static files I tried to google and found that adding a server side route might help. So, I added the following code on server's router.js.

Router.map(function() {
    this.route('files', {
        path: '/files/:path(*)',
        action: function() {
            var path = this.params.path;

            console.log('will serve static content @ '+path);
            this.response.sendfile(path);
        }
    });
});

When I try to access http://localhost:3000/files/someImage.png, it says that no route is defined for /files/someImage.png. Am I doing something wrong? Is there any other way to serve static files using iron router?

Erratic answered 24/1, 2014 at 19:51 Comment(1)
Pay attention not to serve big files using this, it's not dealing well with large files.Carnallite
S
61

Instead of doing all this, you can just put the files under your public directory. If you add the file:

myApp/public/images/kitten.png

You can access it from your templates like:

<img src="/images/kitten.png">

No routes are needed to make that work.

Beware of overlooking the lead slash.

<img src="images/kitten.png">

the above example will work from your top level routes like /books which makes it easy to miss, but fail on /books/pages.

Shimberg answered 24/1, 2014 at 19:57 Comment(6)
Before moving to iron-router that's how I used to manage static files. But, now that my app is becoming multi page, I moved to iron-router. Iron router treats the request coming for /images/kitten.png as yet another route and throws 404 when it does not find one. Therefore, my question is how to server any static content post the movement.Erratic
Something is wrong with the way iron-router is configured then. Without seeing the whole configuration I can't say what it is but you could try removing all of your routes and adding them back one at a time until you find the issue.Shimberg
I am trying to use the concept mentioned at iron router doc. As of now I don't have any other routes. Not even client side routing.Erratic
Then maybe it's another package. It may be useful to debug the problem in reverse - start with a blank project and add code and packages until you see it break. I'm sure you'll discover the problem if you work it systematically like that.Shimberg
Ok, no I think this is the correct behavior. After the linker branch got in, public gets compiled into a folder with a manifest: program.json with hashes, locations etc. so that files added AFTER deploy are no longer dynamically available.Hereditament
Meh. This would've been the correct answer if iron router didn't have a bug that prevents it from working properly. As of 3 Sept 2015 there still are issues with it.Naca
H
5

You probably just need to make sure that the route is in a common area visible on both client and server (This route actually runs on the server) and specify where: 'server'. In addition we need to load the actual file off of disk, which means we need the actual path on the server to the file, and we need to load the 'fs'.

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

Router.map(function() {
  this.route('files', {
    path: '/files/:path(*)',
    where: 'server',
    action: function() {
        var path = this.params.path;
        var basedir = '~/app/';

        console.log('will serve static content @ '+ path);

        var file = fs.readFileSync(basedir + path);

      var headers = {
        'Content-type': 'image/png',
        'Content-Disposition': "attachment; filename=" + path
      };

      this.response.writeHead(200, headers);
      return this.response.end(file);
    }
  });
});

A couple of notes: You should probably move the actual download code into a server only function and call that instead, passing the response in to the function.

You should also do some validation of the path so that you aren't allowing anyone to arbitrarily download files on your server.

In addition, this is setting the content type explicitly, which you will probably want to do for images, but maybe not for other content types. For a generic type you can set it to application/octet-stream

Of course as was mentioned, if your only goal is to have some static content available at deploy time, you should just use the /public directory.

Hereditament answered 17/7, 2014 at 22:10 Comment(0)
T
4

This is a bug in iron-router, which they say is fixed, but I'm still encountering the problem and others have reported on that github issue that it's still a problem.

Edit: It seems to work for files that are directly in /public, but not for files in folders within /public.

Triboelectricity answered 16/4, 2015 at 23:24 Comment(1)
I have jpg files in /public/images/ and use <img src="/images/whatever.jpg"> which works.Tempietempla

© 2022 - 2024 — McMap. All rights reserved.