Victor Leung
Victor Leung
BlogFlower shop

How to upload files with Meteor.js?

December 22, 2014

NOTE: This tutorial is using Meteor 1.1.3 with cfs:standard-pacage 0.0.2 (15th December, 2014). Since this feature is under active development, it maybe further changed by the time you read this. Feel free to contact me if you have any questions;)

The Problem

Last week, a friend of mine asked me how to handle a file upload with Meteor project. I recommended him using Collection FS, so he followed the README.md instructions on their repo. The file upload feature works fine on localhost, but it doesn’t work once it is deployed to the free meteor testing server. In fact, the whole server would keep refreshing, not even load a page.

My Explanation

This is because FS.filesyetem upload the image to a public folder directory. It is not allowed by the server unless it is properly setup due to security concern. So we could use Grid FS as storage adaptor to insert images to MongoDB.

My Solution

Firstly, for installation, instead of using cfs:filesystem, package, use cfs:gridfs

    meteor add cfs:standard-packages
    meteor add cfs:gridfs

Secondly, for syntax, instead of using FS.Collection, change it to FS.Store.GridFS when you declare your collection:

    var imageStore = new FS.Store.GridFS(“images”);

    Images = new FS.Collection(“images”, {
     stores: [imageStore]
    });

Thirdly, setup the deny and allow rules depending on your situation:

    Images.deny({
     insert: function(){
     return false;
     },
     update: function(){
     return false;
     },
     remove: function(){
     return false;
     },
     download: function(){
     return false;
     }
     });

    Images.allow({
     insert: function(){
     return true;
     },
     update: function(){
     return true;
     },
     remove: function(){
     return true;
     },
     download: function(){
     return true;
     }
    });

Next, add a file input button in your client templete for users to click.

    <input type=”file” name=”…” class=”myFileInput”>

And handle the event as follow:

    Template.Profile.events({
       ‘change .myFileInput’: function(event, template) {
          FS.Utility.eachFile(event, function(file) {
            Images.insert(file, function (err, fileObj) {
              if (err){
                 // handle error
              } else {
                 // handle success depending what you need to do
                var userId = Meteor.userId();
                var imagesURL = {
                  “profile.image”: “/cfs/files/images/“ + fileObj._id
                };
                Meteor.users.update(userId, {$set: imagesURL});
              }
            });
         });
       },

Finally, don’t forget the publication/subscription in case you remove autopublish package.

    Meteor.publish(“images”, function(){ return Images.find(); });

And subscribe in your iron:router

    Router.route(‘/profile’,{
     waitOn: function () {
     return Meteor.subscribe(‘images’)
     },
     action: function () {
     if (this.ready())
     this.render(‘Profile’);
     else
     this.render(‘Loading’);
     }
    });

Hope it works for you. In case you are using Amazon S3 bucket, you may want to use cfs:s3 instead as the adapator. And in the worst case it all doeesn’t work for you, Filepicker would be an alternative approach to handle file upload for your reference: https://www.filepicker.io/


About Victor Leung

Software development professional with expertise in application architecture, cloud solutions deployment, and financial products development. Possess a Master's degree in Computer Science and an MBA in Finance. Highly skilled in AWS (Certified Solutions Architect Professional, Developer and SysOps Administrator), GCP (Professional Cloud Architect), Microsoft Azure, Kubernetes(CKA, CKAD, CKS, KCNA), and Scrum(PSM, PSPO) methodologies.

Happy to connect
LinkedIn
Github
Twitter
@victorleungtw

Continuous improvement

Copyright © victorleungtw.com 2023.