The data browser is built for Chrome, Firefox, Safari, and IE9 and above. Please upgrade your browser, or download Google Chrome to get the best experience.
Back to Questions

Solutions to upload to S3 through Cloud Code?

2 votes     1 answer     798 views     

2

Hello, so, I think I have to go though S3 for two reasons (please correct me if I'm wrong and sorry if it sounds a stupid question):

  • 10MB upload limit
  • 10GB parse space

I'm thinking about setting up Cloud Code to allow uploads, however I would like to hear some opinion before diving in.

I guess there are different solutions:

  • Use a third party server, that will call cloud code once upload finishes
  • Use a simple s3 upload library in cloud code
  • Use AWS SDK in cloud code but requires heavy file editing
  • Use a third party server, called via cloud code that will send the binary for store

In the first scenario I guess I'll handle file uploads and the call back Parse and inform it about the file upload or create/update records at will, this should be doable.

In the second, third and fourth scenarios: is it possible to accept file uploads in cloud functions? Will they be killed after a certain amount of time if the client is not able to send all the data it needs?

I'm open to suggestions, thanks.

1 Answer

3

Kurado,

S3 allows you to direct upload from browser, I found a blog which teach people how to direct upload file to s3 from browser.

http://pjambet.github.io/blog/direct-upload-to-s3/

The article use RoR to show how it work, I just rewrite it using Parse cloud code and jquery file upload. You can check my code below.

Cloud code part:

var Crypto = require('crypto');
var Buffer = require('buffer').Buffer;

var accessKeyId = 'YOUR AWS ACCESSKEY';
var secret_key = 'YOUR AWS SECRETE KEY';
var bucket = 'YOUR BUCKET NAME';
var s3_uploader = {
               "thumb":{"key":"uploads/image/","acl":"public-read","fileType":"png"},
               "cover":{"key":"uploads/image/","acl":"public-read","fileType":"png"},
               "screenshots":{"key":"uploads/image/","acl":"public-read","fileType":"png"},
               "pdf":{"key":"uploads/pdf/","acl":"public-read","fileType":"pdf"}
              };

Parse.Cloud.define("awsInfo", function(request, response) {
  var setting = s3_uploader[request.params.uploadType];
  var date = new Date();
  var keyValue = setting.key+request.params.bookId+"/"+request.params.uploadType+"_"+
            date.getTime()+'.'+setting.fileType;
  var result = {"key":keyValue,
                "acl":setting.acl,
                "policy":s3_upload_policy(setting.acl), 
                "signature":s3_upload_signature(setting.acl)};
  response.success(result);
});

function s3_upload_policy(acl) {
    var d = new Date();
    d.setSeconds(0);
    d.setMilliseconds(0);   
    d.setMinutes(d.getMinutes()+10);
    var policy = {"expiration":d.toISOString(),
                  "conditions":[
                    {"bucket":"tattooextreme"},
                    {"acl":acl},
                    ["starts-with","$key", "uploads/"],
                    {"success_action_status": "201"}
                  ]};
    var buf = new Buffer(JSON.stringify(policy),'utf8');
    var result = buf.toString('base64');

    return result;
}

function s3_upload_signature(acl) {
    return  Crypto.createHmac('sha1',secret_key).update(s3_upload_policy(acl)).digest('base64');
}

Website part(using coffeescript and jquery file upload)

 @$('.direct-upload').each(
        ->
            form = $(this)
            $(this).fileupload
                url:form.attr('action')
                dropZone: form.find('#dropzone')
                type:'POST'
                autoUpload: true
                dataType: 'xml'
                add: (event, data)->
                    $('#upload-modal').modal('show')
                    uploadType = form.parent().attr("id").split('input')[1].toLowerCase()
                    Parse.Cloud.run 'awsInfo', \
                    {"uploadType":uploadType,"bookId":bookId},
                        success: (result) -> 
                            form.find('input[name=key]').val(result.key)
                            form.find('input[name=acl]').val(result.acl)
                            form.find('input[name=policy]').val(result.policy)
                            form.find('input[name=signature]').val(result.signature)
                            data.submit()
                        error: (error) -> 
                            alert error.message

                # send: (e, data) -> 
                #     form.find('.progress').fadeIn()  

                progress: (e, data) -> 
                    percent = Math.round((e.loaded / e.total) * 100)
                    $('#upload-modal').find('.bar').css('width', percent + '%')

                fail: (e, data) -> 
                    $('#upload-modal').modal('hide')

                success: (data) -> 
                    url = $(data).find('Location').text()
                    if form.parent().attr('id') == 'inputScreenshots'
                        image = $($('#inputScreenshots img[src=""]')[0])
                        image.attr("src",url)
                        image.parent().find('.delete').removeClass('hidden')
                    else if form.parent().attr('id') == 'inputPDF'
                        form.parent().find('#pdf-path').html(url)
                    else
                        form.parent().find('img').attr("src",url)

                done: (event, data) ->
                    $('#upload-modal').find('.bar').css('width', '0%')
                    $('#upload-modal').modal('hide')

    )

I am not familiar with javascript, so the code are a little mess, but it works well for me. Note that I included a module called 'crypto', I don't find any document about this module in Parse.com, I use this module because Parse team told me cloud code had this module... Hope they have more detail document about this.

Thanks mate, will look into this solution

- Kurado 7 months ago

How can I do this, if I already have Parse.File?

- Ivan 6 months ago