Meteor Upload File with jQuery File Upload

[EDIT 18/2/2014] Full Cordova support straight out of the box! Follow the guidelines at the project Github page

[EDIT 20/12/2014]: Due to big amount of requests I have added the drag and drop support for file uploads. Currently upload of drag and dropped files cannot be canceled

[EDIT 10/11/2014]: For the most updated documentation to this upload control please go to the project Github page.

In this post I’ll introduce two new packages using which you can easily setup a file upload service to your Meteor server. This solution has following perks:

  1. Uses the famous jQuery file upload system from blueimp.
    1. As a result you can upload any file size (default limit 11GB)
    2. Displays upload progress
    3. Uploads can be canceled
    4. You can upload multiple files
    5. Images can be resized to various sizes using the imagemagick
  2. Saves and serves file from arbitrary folder on your server. This solves problems with hot-code reload, when files are saved into Meteor’s public directory
    1. Possibility to save files to subfolders
    2. Possibility to rename files on the server
  3. Simple configuration and installation! No need to install 10+ packages like with Collection-FS solution

Here is a screenshot of the interface:

Screenshot

Please note that since we are using blueimp’s juery solution, this solution has a full potential of performing client image resizes, chunked uploads, upload resume and more. These features will be gradually added. Pull requests are welcome!

Demo application can be found on Github

Quick Start

Install packages

$ meteor add tomi:upload-server
$ meteor add tomi:upload-jquery

Create directory in the application root

mkdir -p .uploads/tmp

Configure server

//file:/server/init.js
Meteor.startup(function () {
  UploadServer.init({
    tmpDir: process.env.PWD + '/.uploads/tmp',
    uploadDir: process.env.PWD + '/.uploads/'
  })
});

Use template

<template name="yourTemplate">
    {{> upload_bootstrap }}
</template>

DONE!

Installation

The installation is very simple and consists of installing two packages:

$ meteor add tomi:upload-server
$ meteor add tomi:upload-jquery

I have separated these two packages, because often you will want to run your upload service as a separate application. There are several options supported by blueimp, such as Java, ASP, Ruby and more or even node.js Express server installed via NPM. If you wish to use self standing server, install only the tomi:upload-jquery package.

Configuration

Server

First, we need to initialise the server and configure upload paths (see below for explanation of the different options):

//file:/server/init.js
Meteor.startup(function () {
  UploadServer.init({
    tmpDir: '/Users/tomi/Documents/Uploads/tmp',
    uploadDir: '/Users/tomi/Documents/Uploads/',
    getDirectory: function(file, formData) {
      return formData.contentType;
    },
    finished: function(file, folder, formFields) {
      console.log('Write to database: ' + folder + '/' + file);
    }
  })
});

Following options are available for UploadServer.init(options):

Field Type Default Description
tmpDir String null Temporary upload directory
uploadDir String null Path to the upload directory
uploadUrl String ‘/upload/’ Upload route
maxPostSize int 11000000000 Maximum post size (11 GB)
minFileSize int 1 Minimum file size
maxFileSize int 10000000000 Maximum file size (10 GB)
acceptFileTypes RegEx /.+/i, Accepted types of files (e.g. prohibit .exe)
imageTypes RegEx Images which can be resized with Imagemagick (e.g. /\.(gif\|jpe?g\|png)$/i)
imageVersions Object {} Defines the sizes of images which will be converted and saved to upload directory. For example {thumbnailBig: {width: 400, height: 300}, thumbnailSmall: {width: 200, height: 100}}
getDirectory function functions which decides the subdirectory in which the file will be saved. In this function is not defined, no sub-directory is created. For example: function(file, formData) { return '/my/sub/directory'; }
getFileName function Renames the file on the server. In no function is specified, file is saved with the original file name. For example: `function(file, formData) { return ‘Saved-‘ + file; }
finished function Callback

Client

On client we have a possibility to use pre-defined templates, existing for semantic-ui and bootstrap. We can use several upload forms on the page. We can distinguish what kind of content we are uploading, by providing the contentType parameter. We can also limit the file type available for the file picker using parameter fileTypes. Following is an example of how you can include the upload form on your page:

<template name="home">
    <h3>PDFs</h3>
    {{> upload_semantic_ui contentType='pdfs' fileTypes='.pdf' }}
    <h3>Images</h3>
    {{> upload_bootstrap contentType='images' fileTypes='.jpg' multiple=true }}
</template>

If you wish to create your own template for uploading, you can use following call in the rendered callback to set up uploading functionality:

Template['your_upload_template'].rendered = function () {
  Uploader.render.call(this);
};

When using this approach, your template MUST contain following:

  • element with class .uploadFilePicker holding the file picker
  • element with class .uploadProgressHolder holding the file picker
  • element with class .uploadProgressBar showing the progress bar
  • element with class .uploadProgressLabel showing the progress label

If you wish to use custom URL for your uploads this can be configured as following:

Uploader.uploadUrl = 'http://yoururl';

30 thoughts on “Meteor Upload File with jQuery File Upload

  1. Miri says:

    Very useful package. Thanks! I switched to it, because the collectionFS does not support cordova so far. What would be the best way to directly display the uploaded image after it has been uploaded successfully? Also on other devices – how to make full use of Meteor reactivity with your package?

    Thanks and best,

    Miriam

    • Dear Miri

      Thanks for your comments. We are steadily improving functionality of this package and making it more robust. When you download the latest version of the example from https://github.com/tomitrescak/meteor-uploads.git, I have added an example of how to show images after they are uploaded.

      The trick is to hook onto Uploader.finished function and then you can perform following.

      In there I add uploaded file info into Session. Once that is done, you can reactively show the list of uploaded files such as here.

      To get advantage of reactivity you need to fill the data on uploaded files into a collection. The best place to do this is here.

      I hope this helped.

      • I am having difficulty inserting data on uploaded files into a collection. When adding a ‘Collection.insert()’ function into the ‘finished:’ callback, I get the following error:

        “Error: Meteor code must always run within a Fiber. Try wrapping callbacks that you pass to non-Meteor libraries with Meteor.bindEnvironment.”

        I have opened an issue on the meteor-uploads issue tracker.

      • Brylie, you are right. There are several workarounds for this problem, but the “easiest” is to just insert data on client’s finished callback instead on server. See the example here

  2. Thanks for the work on this. Seems like a nice alternative.

    I wasn’t able to get it to work under Cordova on ios. I tried the sample app from github. It works fine on mobile and desktop browsers. Under cordova on iOS it allows me to browse for a file, but nothing happens when you hit the upload button.

    Did you get this to work, Miri?

    I look forward to further development, particularly increased imagemagick functionality if that is on the roadmap.

  3. Alex says:

    How can I grab the uploaded picture if I want to post it via a Meteor method in a form for later retrieval? Example:

    var post = {
    title: $(e.target).find(‘[name=title]’).val(),
    description: $(e.target).find(‘[name=description]’).val(),
    image: ?
    }

    • You can always grab the url of the uploaded picture in a callback. This callback executes on server as well as in client.

      E.g. for the client:

      Uploader.finished = function(index, file) {
      Uploads.insert({file: file.name});
      }

  4. Steve says:

    Thanks for this. Works great, except for when I deploy my app to meteor.com. Any idea how to configure it so it’ll work there?

    • As far as I know, this solution will work on meteor.com only if you are uploading into your “public” folder, since you have no write priviledges to write into any other folder. Unfortunately this will trigger the hot code reload. My solution is aimed to be used on dedicated servers, easily deployed with ‘Meteor Up’ by Arunoda.

  5. Steve says:

    Thanks for your response! I was afraid of that. My intention was to move toward a PaaS like Modulus at some point. Are you familiar with Modulus by chance and how easy it would be to configure your package to work with it?

    • I am not very familiar with modulus. Yet, my package can do whatever is permitted on the server. So, the moment you have a write permitted directory on modulus server, you can write into it!

      • Steve says:

        Yea I meant to update this blog yesterday. At the time of this writing, Modulus provides a local storage option (free, but limited size and only accessible via the local instance) and a cloud option which essentially acts like dir you have access to (charged per GB, but no limit and accessible across instances). I tried it real quick and files were indeed uploaded (although I have a couple kinks to work out).

        Thanks again for your time! I figured I’d ask before I spent the money, but it appears as though everything will work fine with Modulus.

  6. it doesn’t work for me , i did exactly what you described :

    here the error on client console :

    TypeError: Cannot read property ‘autoStart’ of null
    at Template.upload.created (http://genjurosama-199720.euw1-2.nitrousbox.com/packages/tomi_upload-jquery.js?b6bf3c0cc365d2eb8f9acc93ee3dcc4bfd892687:2569:29)
    at null. (http://genjurosama-199720.euw1-2.nitrousbox.com/packages/blaze.js?efa68f65e67544b5a05509804bf97e2c91ce75eb:2995:20)
    at fireCallbacks (http://genjurosama-199720.euw1-2.nitrousbox.com/packages/blaze.js?efa68f65e67544b5a05509804bf97e2c91ce75eb:1835:16)
    at Object.Tracker.nonreactive (http://genjurosama-199720.euw1-2.nitrousbox.com/packages/tracker.js?517c8fe8ed6408951a30941e64a5383a7174bcfa:513:12)
    at http://genjurosama-199720.euw1-2.nitrousbox.com/packages/blaze.js?efa68f65e67544b5a05509804bf97e2c91ce75eb:1832:13
    at Object.Blaze._withCurrentView (http://genjurosama-199720.euw1-2.nitrousbox.com/packages/blaze.js?efa68f65e67544b5a05509804bf97e2c91ce75eb:2043:12)
    at Object.Blaze._fireCallbacks (http://genjurosama-199720.euw1-2.nitrousbox.com/packages/blaze.js?efa68f65e67544b5a05509804bf97e2c91ce75eb:1831:9)
    at Object.Blaze._createView (http://genjurosama-199720.euw1-2.nitrousbox.com/packages/blaze.js?efa68f65e67544b5a05509804bf97e2c91ce75eb:1849:9)
    at Object.Blaze._materializeView (http://genjurosama-199720.euw1-2.nitrousbox.com/packages/blaze.js?efa68f65e67544b5a05509804bf97e2c91ce75eb:1853:9)
    at Blaze._DOMMaterializer.def.visitObject (http://genjurosama-199720.euw1-2.nitrousbox.com/packages

  7. Chetan says:

    Thanks Tomi thanks for the help.I really appreciate it.I am trying to limit to number of images uploaded to 20 but even after you help i am not able to figure it out completely.will try and let you know if i can figure it out.thanks for the help tough.

  8. Miri says:

    I would like to use the package to upload a file that I get for example from using the device camera (without using the file picker). I guess I have to use Uploader.startUpload.call(Template.instance(), e);

    But what would be the easiest parameters to just use it with a file (image.jpeg)? Would be great if you have a hint.

    Thanks!

Leave a comment