Saving image to Public/ in production

Hey there!

I’m trying to save an image in the public directory of Meteor for use in production.

I’m using this code for production

var path = process.env["PWD"] + "/bundle/programs/web.browser/app/";
    fs.writeFile(path +'/' +id+'.jpeg', imageBuffer,
    function (err) {
      if (err) throw err;
      console.log('Done!');
    }

It saves the fie to the folder where all my other public assets are, however I cannot access them like I can the others.

Is there a different directory I’m supposed to be saving too? Here are my export if this helps

export PATH=/opt/local/bin:/opt/local/sbin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin 
export NODE_PATH=/usr/lib/nodejs:/usr/lib/node_modules:/usr/share/javascript 
export PWD=/home/app
export HOME=/home/app 
export BIND_IP=127.0.0.1 
export PORT=8080 
export HTTP_FORWARDED_COUNT=1 
export MONGO_URL=mongodb://localhost:27017/app 
export ROOT_URL=https://redacted.com

Cheers for any help you can provide!

Not what you’re asking but I’d strongly recommend not saving things on the server. Use some kind of file storage service instead. The reason is that maintenance becomes a nightmare when you have files stored in the server. e.g What happens if your app needs to use 2 or more servers? Even if you don’t plan on using more than one server, being able to rebuild or redeploy a server without worrying about losing data is a huge peace of mind.

1 Like

Cheers for the reply! If this were for a larger application I’d store files in s3. Fortunately this is for a guild website which probably won’t require more than ~50 images stored during its entire life.

I’m also building this website for a friend and his guild. I’m not sure if I can convince them to use s3 or google cloud, so storing via server seems like the safest thing to do at this phase (while I try to convince them)

I think you should use Meteor.absolutePath for path.

If you want to use img src you need the images uploaded to public folder. But this will cause meteor server to restart. In fact uploading or copying a file anywhere in meteor folder will cause restart or refreshing the client. Except folders that name starts with “.” (eg: .images)

You will also need to set up a route for img src urls. Here is a sample route that I am using for uploaded or exported files.

Router.map(function() {
    this.route('files', {
        path: '/files/:path',
        where: 'server',
        action: function() {
            var path = Meteor.absolutePath + '\\.static~\\' + this.params.path;
            var fs = Npm.require('fs');
            var file = fs.readFileSync(path);
            var extention = /[^.]+$/.exec(path);
            var contentType = "";
            switch(extention){
                case "apk":
                    contentType = "application/vnd.android.package-archive";
                    break;
                case "png":
                case "jpeg":
                case "jpg":
                    contentType = 'image/' + extention;
                    break;
                case "xls":
                    contentType = 'application/vnd.ms-excel';
                    break;
                case "xlsx":
                    contentType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
                    break;
                default:
                    break;
            }

            var headers = {
                'Content-type': contentType,
                'Content-Disposition': "attachment; filename=" + this.params.path
            };

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

Better to add 404 condition to that sample, in my case I always have control over requested file names so I don’t need it.
If you want to serve files which are not in a sub directory of your application make sure you implement it securely. Node can access and serve any file on the disk to anyone if you don’t limit it.

Or you put nginx as a reverse proxy in front of Meteor and let nginx handle the static files.

Then you don’t need to mess around with the router in your application as well.

Cheers for the help! I’m using Flowrouter, but your solution pointed me in the right direction!

I’m using a modified version of

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

Found here
On my server, as flowrouter can only run in Lib. Works like a charm :slight_smile:

Thanks for the suggestion! I’ll go this route on my next project :slight_smile: