Last active
December 30, 2015 01:49
-
-
Save nrmitchi/7758165 to your computer and use it in GitHub Desktop.
Client-side Upload to S3
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Note that this is rather specific to what I was doing due to things like endpoints and message bodies. You will have to change this to fit your purposes | |
*/ | |
function uploadObject () { | |
//This is so that I can access the object from within the XMLHTTPRequest event functions | |
var that = this; | |
this.setCallback = function (cb) { | |
that.cb = cb; | |
console.log('cb set as: '+cb); | |
}, | |
this.uploadFile = function(obj) { | |
// I should put something here to check for duplicates (same date in same session) | |
// Server-side should prevent multiple schedules on same day, however checking here will inprove UX | |
console.log('trying to cors...'); | |
//Switch this to jQuery | |
var file = document.getElementById('file').files[0]; | |
var fd = new FormData(); | |
fd.append('key', obj.key); | |
fd.append('acl', obj.acl); | |
fd.append('Content-Type', 'application/pdf'); | |
fd.append('AWSAccessKeyId', obj.s3Key); | |
fd.append('policy', obj.s3PolicyBase64) | |
fd.append('signature', obj.s3Signature); | |
fd.append("file",file); | |
var xhr = new XMLHttpRequest(); | |
xhr.upload.addEventListener("progress", this.uploadProgress, false); | |
xhr.addEventListener("load", this.uploadComplete, false); | |
xhr.addEventListener("error", this.uploadFailed, false); | |
xhr.addEventListener("abort", this.uploadCanceled, false); | |
// Display loading icon | |
xhr.open('POST', 'https://<your-bucket>.s3.amazonaws.com/', true); //MUST BE LAST LINE BEFORE YOU SEND | |
xhr.send(fd); | |
}, | |
this.uploadProgress = function(evt) { | |
if (evt.lengthComputable) { | |
var percentComplete = Math.round(evt.loaded * 100 / evt.total); | |
console.log('percentComplete: '+percentComplete); | |
//document.getElementById('progressNumber').innerHTML = percentComplete.toString() + '%'; | |
} else { | |
//document.getElementById('progressNumber').innerHTML = 'unable to compute'; | |
} | |
}, | |
this.uploadComplete = function(evt) { | |
console.log('Upload complete! Id: ' + that.id ); | |
$.ajax({ | |
url : '/verifyUpload', | |
type : 'POST', | |
data : 'x='+that.id, | |
success : function (res) { | |
//Fetch the new schedule and add it | |
console.log('Upload verified! Id: '+that.id); | |
Scheduleme.ScheduleListView.loadByDate(that.date); | |
}, | |
error : function (res) { | |
console.log('Upload could not be verified. Id: '+that.id); | |
}, | |
complete : function (res) { | |
that.cb(); | |
} | |
}); | |
}, | |
this.uploadFailed = function(evt) { | |
alert("There was an error attempting to upload the file." + evt); | |
that.cb(); | |
}, | |
this.uploadCanceled = function (evt) { | |
alert("The upload has been canceled by the user or the browser dropped the connection."); | |
that.cb(); | |
}, | |
this.requestCredentials = function () { | |
var date = new Date($('#upload-schedule-date').val()); | |
date = Scheduleme.helpers.UTCify(date); | |
console.log('Date: '+date.toISOString()); | |
//var data = 'date='+date+'&type='+$('#upload-schedule-type').val()+'&timezone='+date.getTimezoneOffset(); | |
var data = { | |
date: date, | |
type: $('#upload-schedule-type').val(), | |
timezone: date.getTimezoneOffset() | |
} | |
console.log('Data: '+data); | |
var that = this; | |
that.date = $('#upload-schedule-date').val(); | |
$.ajax({ | |
url: '/schedules/client-upload', | |
type: 'POST', | |
data: JSON.stringify(data), | |
beforeSend: function (request) { | |
request.setRequestHeader("Content-Type", 'application/json'); | |
}, | |
success: function (res) { | |
console.log('received success'); | |
//that.processResponse(res); | |
that.id = res.id; | |
that.uploadFile(res); | |
}, | |
error: function(jqxhr) { | |
var res = JSON.parse(jqxhr.responseText); | |
alert('Upload failed for the following reason: '+res.message || 'unknown'); | |
console.log('received an error: '+res); | |
that.cb(); | |
} | |
}); | |
} | |
}; | |
// Request credentials from the server | |
var obj = new uploadObject(); | |
obj.setCallback(function () { | |
$('.modal-footer button').attr('disabled', false); | |
$('#ajax-loader').addClass('hidden'); | |
$('#file').val(''); | |
$('#upload-schedule-date').val(''); | |
}); | |
obj.requestCredentials(); | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
exports.clientUpload = function(req, res) { | |
var sendCreds = function (id, key) { | |
var createS3Policy; | |
var s3Signature; | |
var _date, _s3Policy; | |
_date = new Date(); | |
s3Policy = { | |
"expiration": "" + (_date.getFullYear()) + "-" + (_date.getMonth() + 1) + "-" + (_date.getDate()) + "T" + (_date.getHours()+12) + ":" + (_date.getMinutes()) + ":" + (_date.getSeconds()) + "Z", | |
"conditions": [ | |
{ "bucket": "schedule-me" }, | |
[ "starts-with", "$key", ""], | |
{ "acl": "public-read" }, | |
["content-length-range", 0, 2147483648], | |
["eq", "$Content-Type", 'application/pdf'] | |
] | |
}; | |
var s3PolicyBase64 = new Buffer( JSON.stringify( s3Policy ) ).toString( 'base64' ); | |
// I should change the redirect to verifyUpload/:id | |
var s3Credentials = { | |
key: key, | |
acl: 'public-read', | |
s3PolicyBase64: s3PolicyBase64, | |
s3Signature: crypto.createHmac( 'sha1', process.env.AWS_SECRET_ACCESS_KEY ).update( s3PolicyBase64 ).digest( 'base64' ), | |
s3Key: process.env.AWS_ACCESS_KEY_ID , | |
s3Redirect: "http://yourdomain.herokuapp.com/verifyUpload?x="+id, | |
s3Policy: s3Policy, | |
id: id | |
}; | |
res.end(JSON.stringify(s3Credentials)); | |
}; | |
var file_name = new Date().getTime().toString(); //Use the current time as key for testing | |
var rand = 'dflkasjceo;ajsclkajs'; //Random string | |
file_name = crypto.createHmac('sha1', rand).update(file_name).digest('hex')+'.pdf'; | |
var model = new Model({ | |
/* | |
attributes... | |
*/ | |
}); | |
model.save(function (err) { | |
var response = {}; | |
if (err) { | |
if (model._invalid) { | |
response.statusCode = 400; | |
} else { | |
response.statusCode = 500; | |
} | |
response.error = err; | |
Main.Render.code(req, res, response ) | |
} else { | |
sendCreds(schedule.id, file_name); | |
} | |
}); | |
}; | |
exports.verifyUpload = function (req, res) { | |
var id = req.body.x || req.query.x; | |
if (typeof id != 'undefined') { | |
console.log('Verifying model: '+id); | |
//This update hangs and i'm not sure why.... I feel like it may be because it doens't have a db connection but I cant seem to figure it out | |
Model.verifyUpload(id, function (obj) { | |
err = obj.err; | |
result = obj.result; | |
if (err) { | |
Main.Logger.error(err); | |
Main.Render.code(req.xhr, res, { statusCode: 500 } ); | |
} else { | |
Main.Render.code(req.xhr, res, { statusCode: 200 } ); | |
} | |
}); | |
} else { | |
//Do nothing, no parameter supplied | |
console.log('No id provided'); | |
Main.Render.code(req.xhr, res, { statusCode: 400 } ); | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment