(function(global) { global.S3MP = (function() { // Wrap this into underscore library extension _.mixin({ findIndex : function (collection, filter) { for (var i = 0; i < collection.length; i++) { if (filter(collection[i], i, collection)) { return i; } } return -1; } }); // S3MP Constructor function S3MP(options) { var files , progress_timer = [] , S3MP = this; _.extend(this, options); this.uploadList = []; // Handles all of the success/failure events, and // progress notifiers this.handler = { // Activate an appropriate number of parts (number = pipes) // when all of the parts have been successfully initialized beginUpload: function() { var i = []; function beginUpload(pipes, uploadObj) { var key = uploadObj.key , num_parts = uploadObj.parts.length if (typeof i[key] === "undefined") { i[key] = 0; } i[key]++; if (i[key] === num_parts) { for (var j=0; j 1000000000) { // size greater than 1gb num_segs = 100; pipes = 10; } else if (this.size > 500000000) { // greater than 500mb num_segs = 50; pipes = 5; } else if (this.size > 100000000) { // greater than 100 mb num_segs = 20; pipes = 5; } else { // greater than 5 mb (S3 does not allow multipart uploads < 5 mb) num_segs = 2; pipes = 2; } chunk_segs = _.range(num_segs + 1); chunk_lens = _.map(chunk_segs, function(seg) { return Math.round(seg * (file.size/num_segs)); }); if (upload.sliceBlob == "Unsupported") { this.parts = [new UploadPart(file, 0, upload)]; } else { this.parts = _.map(chunk_lens, function(len, i) { blob = upload.sliceBlob(file, len, chunk_lens[i+1]); return new UploadPart(blob, i+1, upload); }); this.parts.pop(); // Remove the empty blob at the end of the array } // init function will initiate the multipart upload, sign all the parts, and // start uploading some parts in parallel this.init = function() { upload.initiateMultipart(upload, function(obj) { var id = upload.id = obj.id , upload_id = upload.upload_id = obj.upload_id , object_name = upload.object_name = obj.key // uuid generated by the server, different from name , parts = upload.parts; upload.signPartRequests(id, object_name, upload_id, parts, function(response) { _.each(parts, function(part, key) { var xhr = part.xhr; xhr.open('PUT', 'http://'+upload.bucket+'.s3.amazonaws.com/'+object_name+'?partNumber='+part.num+'&uploadId='+upload_id, true); xhr.setRequestHeader('x-amz-date', response[key].date); xhr.setRequestHeader('Authorization', response[key].authorization); // Notify handler that an xhr request has been opened upload.handler.beginUpload(pipes, upload); }); }); }); } }; // Inherit the properties and prototype methods of the passed in S3MP instance object Upload.prototype = o; return new Upload(); } // Upload part constructor function UploadPart(blob, key, upload) { var part, xhr; part = this; this.size = blob.size; this.blob = blob; this.num = key; this.xhr = xhr = upload.createXhrRequest(); xhr.onload = function() { upload.handler.onPartSuccess(upload, part); }; xhr.onerror = function() { upload.handler.onError(upload, part); }; xhr.upload.onprogress = _.throttle(function(e) { upload.inprogress[key] = e.loaded; }, 1000); }; UploadPart.prototype.activate = function() { this.xhr.send(this.blob); this.status = "active"; }; UploadPart.prototype.pause = function() { this.xhr.abort(); this.status = "paused"; }; return S3MP; }()); }(this));