Skip to content

Instantly share code, notes, and snippets.

@nrmitchi
Last active December 30, 2015 01:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nrmitchi/7758165 to your computer and use it in GitHub Desktop.
Save nrmitchi/7758165 to your computer and use it in GitHub Desktop.
Client-side Upload to S3
/*
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();
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