MaximDubrovin / meteor-uploadcare

Uploadcare library wrapped into Meteor package
2 stars 4 forks source link

meteor-uploadcare

Uploadcare library wrapped into a Meteor package.

Uploadcare handles uploads, so you don’t have to! Beautiful upload widget, API to manage files in cloud storage, smart and fast CDN to deliver them to your end users. Crop, resize and transform uploaded images using URL commands.

Real life Meteor app example:

Look at the beautiful Uploadcare integration in Alpheratz — personal gallery service on Meteor.

Install

meteor add maximdubrovin:uploadcare

Start uploading right now:

Set your public key. This can go to the <head> of your Meteor app:

<head>
  ...
  <script>
      UPLOADCARE_PUBLIC_KEY = 'demopublickey';
  </script>
  ...
<head/>

demopublickey will work for testing purpose.

Place this input element somewhere in your templates:

<input type="hidden" name="picture" role="uploadcare-uploader" data-crop />

The library looks for an input with special role attribute, and places a widget there.

As soon as the file is uploaded, this input will receive file UUID or CDN link.

To get file URL and other info after upload is finished, you need to get the widget instance for a given input element.

var widget = uploadcare.Widget('[role=uploadcare-uploader]');

And listen widget.onUploadComplete()

widget.onUploadComplete(function(fileInfo) {
  console.log('fileInfo,' fileInfo);
  console.log('file UUID', fileInfo.uuid);
  console.log('fileInfo.originalUrl', fileInfo.originalUrl); // Public CDN URL of the uploaded file, without any operations.
});

Now you can save file URL and UUID to the database for further usage.

Image operations

You can apply various image operations on uploaded images, such as resize, crop, blur, rotate, return progressive jpeg image, set image quality and much more, by appending CDN commands on the original URL.

Examples of URL image operations:

http://www.ucarecdn.com/c5b7dd84-c0e2-48ff-babc-d23939f2c6b4/-/preview/480x480/-/quality/lightest/

http://www.ucarecdn.com/9eaf4b7b-6688-43e9-a6e5-9690142d765a/-/preview/-/blur/10/

http://www.ucarecdn.com/13448644-f240-4171-bad7-8e079eee491a/-/preview/-/grayscale/

http://www.ucarecdn.com/ec8850a1-7d02-4af0-ad92-dbac0d169408/-/preview/-/quality/best/-/progressive/yes/

Storing and Deleting files via REST API

The widget starts uploading immediately after user chooses a file. It speeds up interaction with your app, and makes UI async, allowing users to do other stuff while the file is still uploading. This can be a real time saver.

By default uploaded files will be available in Uploadcare storage for 24 hours from URL or UUID (via REST API).

Why? To prevent overwhelming your project with unneeded files, to avoid exceeding your plan limits, and to protect you from risk of abusing your public key by malicious users. You may store each uploaded file permanently, or delete it, using REST API on your Meteor back-end.

Since REST API manipulations must be secure, they require both your public and secret keys.

Placing your secret and public keys into Meteor back-end code

Easiest and the most secure way to provide your Uploadcare keys to Meteor back-end code is placing them to Meteor.settings by creating a settings.json file:

{
  "uploadcare": {
    "public_key": "xxx...",
    "secret_key": "yyy..."
  }
}

Use the command to run local Meteor app with these settings:

meteor --config /path/to/settings.json

See notes on deploying Meteor app with settings.json at Meteor Up tool.

Add HTTP package

To use Uploadcare REST API within Meteor back-end code, you have to make HTTP requests to Uplodacare infrastructure using HTTP package:

meteor add http

Storing and Deleting files

Client-side call to your back-end:

Meteor.call('storeOnUplodcare', uuid, function(err, res) {});
Meteor.call('deleteFromUploadcare', uuid, function(err, res) {});

Server-side method:

Future = Npm && Npm.require('fibers/future');

Meteor.methods({

    storeOnUplodcare: function(uuid)
    {
        check(uuid, String);

        this.unblock();

        var future = new Future();

        HTTP.call(
            'PUT',
            'https://api.uploadcare.com/files/' + uuid + '/storage/',
            {
                headers: {
                    Accept: 'application/vnd.uploadcare-v0.3+json',
                    Date: new Date().toJSON(),
                    Authorization: 'Uploadcare.Simple ' + Meteor.settings.uploadcare.public_key + ':' + Meteor.settings.uploadcare.secret_key
                }
            },
            function(err)
            {
                if (err)
                {
                    future.return(err, null)
                }
                else
                {

                    future.return(null, true)
                }
            }
        );

        return future.wait();
    },

    deleteFromUploadcare: function(uuid)
    {
        check(uuid, String);

        this.unblock();

        var future = new Future();

        HTTP.call(
            'DELETE',
            'https://api.uploadcare.com/files/' + uuid + '/',
            {
                headers: {
                    Accept: 'application/vnd.uploadcare-v0.3+json',
                    Date: new Date().toJSON(),
                    Authorization: 'Uploadcare.Simple ' + Meteor.settings.uploadcare.public_key + ':' + Meteor.settings.uploadcare.secret_key
                }
            },
            function(err)
            {
                if (err)
                {
                    future.return(err, null)
                }
                else
                {
                    future.return(null, true)
                }
            }
        );

        return future.wait();
    }

});

Notes:

Place back-end code in the following directory of your Meteor project: meteor-app/server/methods_server.js. It will prevent serving this code to client.

I use fibers/future to get async callbacks on client.

Since any UUID can be passed to deleteFromUploadcare(uuid) and storeOnUplodcare(uuid), add checks in the back-end code which ensure that user stores/deletes files that belongs to him.

Example:

Meteor.methods({
    userIsAlbumItemImageUUIDowner: function(uuid)
    {
        check(uuid, String);

        return AlbumItems.find(
            {
                owner: this.userId,
                uuid: uuid
            }
        ).count();
    },
    storeOnUplodcare: function(uuid)
    {
        check(uuid, String);

        if (!Meteor.call('userIsAlbumItemImageUUIDowner', uuid))
            return;

        ...
    }
})