How to upload files with Meteor.js?

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/


Originally published at victorleungtw.com on December 15, 2014.

victorleungtw

Years of experience delivering enterprise projects with global stakeholders in banking, airline and gaming industries involving a broad range of technologies. Provide consulting services at both executive and operational levels, including strategy and data analysis. Lead web and mobile app development team with hands-on coding implementation. Manage projects as Certified Scrum Master with Agile techniques. Qualification with master degree in Computer Science and pursuing MBA degree in Finance.

Leave a Reply

Close Menu