How would one handle a file upload with Meteor?

JavascriptFile UploadMeteor

Javascript Problem Overview


What would be the canonical way to handle a file upload with Meteor?

Javascript Solutions


Solution 1 - Javascript

I used http://filepicker.io. They'll upload the file, store it into your S3, and return you a URL where the file is. Then I just plop the url into a DB.

  1. Wget the filepicker script into your client folder.

     wget https://api.filepicker.io/v0/filepicker.js
    
  2. Insert a filepicker input tag

     <input type="filepicker" id="attachment">
    
  3. In the startup, initialize it:

     Meteor.startup( function() {
         filepicker.setKey("YOUR FILEPICKER API KEY");
         filepicker.constructWidget(document.getElementById('attachment'));
     });
    
  4. Attach a event handler

     Templates.template.events({
         'change #attachment': function(evt){
             console.log(evt.files);
         }
     });
    

Solution 2 - Javascript

For images, I use a method similar to Dario's except I don't write the file to disk. I store the data directly in the database as a field on the model. This works for me because I only need to support browsers that support the HTML5 File API. And I only need simple image support.

Template.myForm.events({
  'submit form': function(e, template) {
    e.preventDefault();
    var file = template.find('input type=["file"]').files[0];
    var reader = new FileReader();
    reader.onload = function(e) {
      // Add it to your model
      model.update(id, { $set: { src: e.target.result }});
  
      // Update an image on the page with the data
      $(template.find('img')).attr('src', e.target.result);
    }
    reader.readAsDataURL(file);
  }
});

Solution 3 - Javascript

I've just come up with an implementation of file uploads using Meteor.methods and HTML5 File's API. Let me know what you think.

Solution 4 - Javascript

There currently doesn't seem to be a way to interact with the HTTP server or do anything related to HTTP.

The only things you can do is talk to server over the RPC methods exposed by Meteor.methods or talk to mongoDB directly over the mongoDB API exposed.

Solution 5 - Javascript

There is a new package: edgee:slingshot. It does not upload the files to your meteor server, but it is better that way as it allows the meteor server to focus on its primary objective of serving the meteor app instead of handling costly file transfers.

Instead it uploads files to cloud storage services. Currently it supports AWS S3 and Google Cloud Files, but it will also support Rackspace Cloud Files and perhaps Cloudinary in the future.

Your meteor server merely acts as as a coordinator.

Direct VS Indirect uploads

It is also a very versatile and light-weight package.

Solution 6 - Javascript

there is an atmosphere package called router which allows just that.

actually, the best way to handle file uploads is now collectionFS

Solution 7 - Javascript

Here is the best solution for this time. It uses collectionFS.

meteor add cfs:standard-packages
meteor add cfs:filesystem

Client:

Template.yourTemplate.events({
    'change .your-upload-class': function(event, template) {
        FS.Utility.eachFile(event, function(file) {
            var yourFile = new FS.File(file);
            yourFile.creatorId = Meteor.userId(); // add custom data
            YourFileCollection.insert(yourFile, function (err, fileObj) {
                if (!err) {
                   // do callback stuff
                }
            });
        });
    }
});

Server:

YourFileCollection = new FS.Collection("yourFileCollection", {
    stores: [new FS.Store.FileSystem("yourFileCollection", {path: "~/meteor_uploads"})]
});
YourFileCollection.allow({
    insert: function (userId, doc) {
        return !!userId;
    },
    update: function (userId, doc) {
        return doc.creatorId == userId
    },
    download: function (userId, doc) {
        return doc.creatorId == userId
    }
});

Template:

<template name="yourTemplate">
    <input class="your-upload-class" type="file">
</template>

Solution 8 - Javascript

If you do not require significantly large files or maybe only storing the files for a short period of time then this simple solution works very well.

In your html...

<input id="files" type="file" />

In your template event map...

Template.template.events({
  'submit': function(event, template){
    event.preventDefault();
    if (window.File && window.FileReader && window.FileList && window.Blob) {
      _.each(template.find('#files').files, function(file) {
        if(file.size > 1){
          var reader = new FileReader();
          reader.onload = function(e) {
            Collection.insert({
              name: file.name,
              type: file.type,
              dataUrl: reader.result
            });
          }
          reader.readAsDataURL(file);
        }
      });
    }
  }
});

Subscribe to the Collection and in a template render a link...

<a href="{{dataUrl}}" target="_blank">{{name}}</a>

While this might not be the most robust or elegant solution for large files or a file intensive application it works very well for all kind of file formats if you want to implement simple upload and download/rendering of the files.

Solution 9 - Javascript

You could try uploading directly to amazon S3, doing some tricks with js uploaders and stuff. http://aws.amazon.com/articles/1434

Solution 10 - Javascript

You can see on the meteor roadmap that the feature "File upload pattern" is scheduled for "After 1.0". So we have to wait to see an official way.

For now, one of the best ways is to use "collectionFS" (which is 0.3.x dev preview at the time of writting).

Or inkfilepicker (ex. filepicker.io) as suggested here. It is easy enough to use, although this obviously requires and Internet connection from the user side.

If it just to play around, you could as well take advantage of the html5 feature. Something like that.

Solution 11 - Javascript

To accomplish the same action as the most upvoted answer without the cost of filepicker.io, follow the instructions for this package: https://github.com/Lepozepo/S3

Then to obtain the link, use code similar to below. Finally, plug the url returned by secureLink into the DB.

Template.YourTemplate.events({
  "click button.upload": function() {
    var files = $("input.file_bag")[0].files;
    S3.upload(files, "/subfolder", function(e,r) {
      console.log(r);
      Session.set('secureLink', r.secure_url);
    })
  }
});

Template.YourTemplate.helpers({
  "files": function() {
    return S3.collection.find();
  },
  
  "secureLink": function() {
    return Session.get('secureLink');
  }
});

Solution 12 - Javascript

here is yet another solution:

https://doctorllama.wordpress.com/2014/11/06/meteor-upload-package-with-jquery-file-upload/

This one is using Blueimp's upload solution that supports chunked uploads, progress bars and more.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDavidView Question on Stackoverflow
Solution 1 - Javascriptjlg_foilView Answer on Stackoverflow
Solution 2 - JavascriptHarry LoveView Answer on Stackoverflow
Solution 3 - JavascriptDarío Javier CraveroView Answer on Stackoverflow
Solution 4 - JavascriptRaynosView Answer on Stackoverflow
Solution 5 - Javascriptd_inevitableView Answer on Stackoverflow
Solution 6 - JavascriptMicha RoonView Answer on Stackoverflow
Solution 7 - JavascriptRazView Answer on Stackoverflow
Solution 8 - JavascriptSteeve CannonView Answer on Stackoverflow
Solution 9 - JavascriptLuanView Answer on Stackoverflow
Solution 10 - JavascriptnhaView Answer on Stackoverflow
Solution 11 - JavascriptSean LView Answer on Stackoverflow
Solution 12 - JavascripttomitrescakView Answer on Stackoverflow