Native Image Uploading with Meteor 1.3


I was surprised by how little there is out there, when I did a search for this. And some of the native solutions were pretty outlandish. My goal is simply to upload an image file, and store it in mongoDB from Meteor. The upload will be activated by the user clicking an icon. The icon is just an HTML <img /> tag, followed by a hidden <input /> of type “file”, so the basics look like this:


<label id="photo" for="textarea">
  <img src="/img/icon.png" />
  <input id="ul" type="file" />
</label>

So, nothing fancy. But, we need a way to click that file input that’s hiding:


var ibin = '';
Template.postBox.events({
  'click #photo'( evt, tmpl ) {
    $('#ul').click();
  },
});

When the user clicks the icon image, it’s click event calls the click event of our hidden file input.

Now, without making things harder than they have to be, what is really needed, to get an image saved into our DB? First, let’s narrow the parameters. In this article, we’re just addressing jpeg’s and png’s, and we’re not doing any sanity checking. We’ll be saving the image in binary form (obviously), that can easily be served to a page.

So then, how to capture the upload? When the user clicks, a file dialog opens so that the user can select the file. It’s not until (s)he selects a file, do we have access to what it is. Logically enough, this information can be captured in the change event. It’s the hidden file input that’s causing the dialog to be displayed, so it is it’s change event we want. Note that it has an id of ul. First, we capture the event, and lets grab the file’s extension (this will go directly under the above code):


  ...
  'change #ul'( evt, tmpl ) {
    let mark = (evt.currentTarget.files[0].name).lastIndexOf('.') + 1;
    let ext  = (evt.currentTarget.files[0].name).slice( mark );
    ext      = ( ext == ( 'jpg' || 'jpeg' ) ) ? 'jpeg' : 'png';
  }

First, we’re grabbing the period that, in this simplistic example, separates the filename from the extension. Then, we’re slicing off the extension, and comparing it to either jpeg/jpg or png, which again, in this simplistic example are the only possibilities.

There are always many different ways to do something. An optimized way would of course be to grab a reference to the file at the beginning, and then reuse it as needed. But I think it makes more sense when demonstrating something to make it dead simple. You can be clever. Below the code we just wrote, we’ll now add:


  ...
    let fil     = $('#ul').get(0).files[0];
    let fr      = new FileReader();
    fr.onload   = function() {
      ibin      = this.result;
    };
    fr.readAsDataURL( fil );

First, we’re assigning the file blob to the fil variable (using a different way to access it), via jQuery. Next, we create a FileReader. This is a built-in class on real browsers that follow standards, so I don’t know if it works with IE or not…  To the newly created FileReader, we’re adding an onload event handler. It just so happens that the return value comes back in it’s result property.

Now we address the variable that you may have thought was a typo at the very top, above our Template.x.x. This was placed intentionally, as a very quick and easy way to have the variable available throughout. The above code works like this: We take our file blob (fil), and feed it to FileReader#readAsDataURL. FileReader#readAsDataURL accesses the onload handler, and populates our ibin variable with the binary file information that will conveniently be able to be used straight from the db and onto our webpage. Here are the final bits of this event handler (it goes directly under the above snippet):


  ...
    Meteor.setTimeout( function() {
      //clean-up stuff
    }, 1000);
    Newsfeeds.insert( { ext: ext, bin: ibin });
  },
});  //end of Template.postBox.events

You can stuff whatever you want into the Collection (here I’m calling it Newsfeeds). Pretend there are various fields added so that we can identify the poster, date, etc. Here we give our insert a little time to settle prior to running whatever clean-up code we might have.

That’s it! You now have the binary image saved in the Collection, as well as it’s extension.

Now, let’s display the image from the Collection. A word about displaying binary files in HTML. We make use of data:<mime-type> That’s why the extension was saved. The mime type for jpeg’s is image/jpeg, and the mime type for png’s is image/png. So, in our Template file, we can do this:


  ... 
  {{#if bin }} <img src="data:image/{{ext}};base64,{{bin}}" /> {{/if}} 
  ... 

Obviously, you need to have a helper for this template to pull the record you want from the db, and then iterate over whatever fields you want to display in your template. What we’re picking up here is the bin field, and the ext field.

So there it is. No frills image uploading, and display. Plenty is left out, not the least being sanity checks of input coming into the routines, etc. Hopefully this will be useful to someone.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s