s3_multipart 0.0.4 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +11 -0
- data/Gemfile +2 -2
- data/Gemfile.lock +8 -1
- data/README.md +150 -42
- data/Rakefile +10 -0
- data/app/controllers/s3_multipart/application_controller.rb +6 -3
- data/app/controllers/s3_multipart/uploads_controller.rb +30 -29
- data/app/models/s3_multipart/upload.rb +13 -5
- data/grunt.js +44 -0
- data/javascripts/footer.js +5 -0
- data/javascripts/header.js +14 -0
- data/javascripts/libs/underscore.js +1 -0
- data/javascripts/s3mp.js +329 -0
- data/javascripts/upload.js +76 -0
- data/javascripts/uploadpart.js +32 -0
- data/lib/generators/s3_multipart/install_generator.rb +30 -0
- data/lib/generators/s3_multipart/templates/add_uploader_column_to_model.rb +7 -0
- data/lib/generators/s3_multipart/templates/aws.yml +4 -0
- data/lib/generators/s3_multipart/templates/configuration_initializer.rb +8 -0
- data/lib/generators/s3_multipart/templates/uploader.rb +29 -0
- data/{db/migrate/20110727184726_create_s3_multipart_uploads.rb → lib/generators/s3_multipart/templates/uploads_table_migration.rb} +2 -0
- data/lib/generators/s3_multipart/uploader_generator.rb +33 -0
- data/lib/s3_multipart/action_view_helpers/form_helper.rb +2 -1
- data/lib/s3_multipart/config.rb +12 -0
- data/lib/s3_multipart/http/net_http.rb +6 -6
- data/lib/s3_multipart/railtie.rb +12 -0
- data/lib/s3_multipart/transfer_helpers.rb +92 -0
- data/lib/s3_multipart/uploader/callbacks.rb +17 -0
- data/lib/s3_multipart/uploader/validations.rb +9 -0
- data/lib/s3_multipart/uploader.rb +27 -68
- data/lib/s3_multipart/version.rb +1 -1
- data/lib/s3_multipart.rb +4 -42
- data/package.json +9 -0
- data/s3_multipart.gemspec +6 -3
- data/spec/integration/uploads_controller_spec.rb +2 -1
- data/spec/internal/app/assets/javascripts/application.js +148 -63
- data/spec/internal/app/assets/stylesheets/application.css.scss +384 -0
- data/spec/internal/app/assets/stylesheets/font-awesome.scss +499 -0
- data/spec/internal/app/controllers/application_controller.rb +4 -1
- data/spec/internal/app/controllers/pages_controller.rb +3 -7
- data/spec/internal/app/models/user.rb +4 -0
- data/spec/internal/app/models/video.rb +5 -0
- data/spec/internal/app/uploaders/multipart/video_uploader.rb +32 -0
- data/spec/internal/app/views/pages/upload.html.erb +38 -4
- data/spec/internal/config/routes.rb +1 -1
- data/spec/internal/db/schema.rb +22 -14
- data/spec/internal/public/fonts/FontAwesome.otf +0 -0
- data/spec/internal/public/fonts/fontawesome-webfont.eot +0 -0
- data/spec/internal/public/fonts/fontawesome-webfont.ttf +0 -0
- data/spec/internal/public/fonts/fontawesome-webfont.woff +0 -0
- data/spec/internal/tmp/cache/sass/077f66d4d9153cfd737b81ab3f4c5d5858b0db3b/_grid-background.scssc +0 -0
- data/spec/internal/tmp/cache/sass/1925334270d3f17f5ea2c4e86092ee0214484038/_appearance.scssc +0 -0
- data/spec/internal/tmp/cache/sass/1925334270d3f17f5ea2c4e86092ee0214484038/_background-clip.scssc +0 -0
- data/spec/internal/tmp/cache/sass/1925334270d3f17f5ea2c4e86092ee0214484038/_background-origin.scssc +0 -0
- data/spec/internal/tmp/cache/sass/1925334270d3f17f5ea2c4e86092ee0214484038/_background-size.scssc +0 -0
- data/spec/internal/tmp/cache/sass/1925334270d3f17f5ea2c4e86092ee0214484038/_border-radius.scssc +0 -0
- data/spec/internal/tmp/cache/sass/1925334270d3f17f5ea2c4e86092ee0214484038/_box-shadow.scssc +0 -0
- data/spec/internal/tmp/cache/sass/1925334270d3f17f5ea2c4e86092ee0214484038/_box-sizing.scssc +0 -0
- data/spec/internal/tmp/cache/sass/1925334270d3f17f5ea2c4e86092ee0214484038/_box.scssc +0 -0
- data/spec/internal/tmp/cache/sass/1925334270d3f17f5ea2c4e86092ee0214484038/_columns.scssc +0 -0
- data/spec/internal/tmp/cache/sass/1925334270d3f17f5ea2c4e86092ee0214484038/_filter.scssc +0 -0
- data/spec/internal/tmp/cache/sass/1925334270d3f17f5ea2c4e86092ee0214484038/_font-face.scssc +0 -0
- data/spec/internal/tmp/cache/sass/1925334270d3f17f5ea2c4e86092ee0214484038/_hyphenation.scssc +0 -0
- data/spec/internal/tmp/cache/sass/1925334270d3f17f5ea2c4e86092ee0214484038/_images.scssc +0 -0
- data/spec/internal/tmp/cache/sass/1925334270d3f17f5ea2c4e86092ee0214484038/_inline-block.scssc +0 -0
- data/spec/internal/tmp/cache/sass/1925334270d3f17f5ea2c4e86092ee0214484038/_opacity.scssc +0 -0
- data/spec/internal/tmp/cache/sass/1925334270d3f17f5ea2c4e86092ee0214484038/_regions.scssc +0 -0
- data/spec/internal/tmp/cache/sass/1925334270d3f17f5ea2c4e86092ee0214484038/_shared.scssc +0 -0
- data/spec/internal/tmp/cache/sass/1925334270d3f17f5ea2c4e86092ee0214484038/_text-shadow.scssc +0 -0
- data/spec/internal/tmp/cache/sass/1925334270d3f17f5ea2c4e86092ee0214484038/_transform.scssc +0 -0
- data/spec/internal/tmp/cache/sass/1925334270d3f17f5ea2c4e86092ee0214484038/_transition.scssc +0 -0
- data/spec/internal/tmp/cache/sass/351072894b53bf1a2d2af4bd57c177c805cf063e/_contrast.scssc +0 -0
- data/spec/internal/tmp/cache/sass/49ecd6d86aba13e15e515be708bbfd8bb67a011a/_css3.scssc +0 -0
- data/spec/internal/tmp/cache/sass/49ecd6d86aba13e15e515be708bbfd8bb67a011a/_support.scssc +0 -0
- data/spec/internal/tmp/cache/sass/49ecd6d86aba13e15e515be708bbfd8bb67a011a/_typography.scssc +0 -0
- data/spec/internal/tmp/cache/sass/49ecd6d86aba13e15e515be708bbfd8bb67a011a/_utilities.scssc +0 -0
- data/spec/internal/tmp/cache/sass/633bc5f017268b81a60f921876c980c4adcebb74/_base.scssc +0 -0
- data/spec/internal/tmp/cache/sass/633bc5f017268b81a60f921876c980c4adcebb74/_sprite-img.scssc +0 -0
- data/spec/internal/tmp/cache/sass/68dbce8ac3037e0cd797c9b5c6aa131096361144/_utilities.scssc +0 -0
- data/spec/internal/tmp/cache/sass/7722315031801f2a146090aa73380dfd28539145/_clearfix.scssc +0 -0
- data/spec/internal/tmp/cache/sass/7722315031801f2a146090aa73380dfd28539145/_float.scssc +0 -0
- data/spec/internal/tmp/cache/sass/7722315031801f2a146090aa73380dfd28539145/_hacks.scssc +0 -0
- data/spec/internal/tmp/cache/sass/7722315031801f2a146090aa73380dfd28539145/_min.scssc +0 -0
- data/spec/internal/tmp/cache/sass/7722315031801f2a146090aa73380dfd28539145/_reset.scssc +0 -0
- data/spec/internal/tmp/cache/sass/7722315031801f2a146090aa73380dfd28539145/_tag-cloud.scssc +0 -0
- data/spec/internal/tmp/cache/sass/775792c99f07f64e98296a8dc8c9d2c74a28fd8a/_reset.scssc +0 -0
- data/spec/internal/tmp/cache/sass/8b4f1545a124f2498699911561d1ec6302c9de56/_ellipsis.scssc +0 -0
- data/spec/internal/tmp/cache/sass/8b4f1545a124f2498699911561d1ec6302c9de56/_force-wrap.scssc +0 -0
- data/spec/internal/tmp/cache/sass/8b4f1545a124f2498699911561d1ec6302c9de56/_nowrap.scssc +0 -0
- data/spec/internal/tmp/cache/sass/8b4f1545a124f2498699911561d1ec6302c9de56/_replacement.scssc +0 -0
- data/spec/internal/tmp/cache/sass/abd68615441476dbd8e9fdc1606210305b3177fd/_bullets.scssc +0 -0
- data/spec/internal/tmp/cache/sass/abd68615441476dbd8e9fdc1606210305b3177fd/_horizontal-list.scssc +0 -0
- data/spec/internal/tmp/cache/sass/abd68615441476dbd8e9fdc1606210305b3177fd/_inline-block-list.scssc +0 -0
- data/spec/internal/tmp/cache/sass/abd68615441476dbd8e9fdc1606210305b3177fd/_inline-list.scssc +0 -0
- data/spec/internal/tmp/cache/sass/b1bb3e2fbcb5c6a71ea2273c6dcb7c8967d2a058/_alternating-rows-and-columns.scssc +0 -0
- data/spec/internal/tmp/cache/sass/b1bb3e2fbcb5c6a71ea2273c6dcb7c8967d2a058/_borders.scssc +0 -0
- data/spec/internal/tmp/cache/sass/b1bb3e2fbcb5c6a71ea2273c6dcb7c8967d2a058/_scaffolding.scssc +0 -0
- data/spec/internal/tmp/cache/sass/bc7f11d6e8f07a22f545deb35760cfc7830f3369/application.css.scssc +0 -0
- data/spec/internal/tmp/cache/sass/bc7f11d6e8f07a22f545deb35760cfc7830f3369/font-awesome.scssc +0 -0
- data/spec/internal/tmp/cache/sass/c9342b40bf8bc7bf64ce587860fb065cbf6d2ca4/_color.scssc +0 -0
- data/spec/internal/tmp/cache/sass/c9342b40bf8bc7bf64ce587860fb065cbf6d2ca4/_general.scssc +0 -0
- data/spec/internal/tmp/cache/sass/c9342b40bf8bc7bf64ce587860fb065cbf6d2ca4/_sprites.scssc +0 -0
- data/spec/internal/tmp/cache/sass/c9342b40bf8bc7bf64ce587860fb065cbf6d2ca4/_tables.scssc +0 -0
- data/spec/internal/tmp/cache/sass/d1da036a47062a9f69ce0327962c00aa77d2ea44/_hover-link.scssc +0 -0
- data/spec/internal/tmp/cache/sass/d1da036a47062a9f69ce0327962c00aa77d2ea44/_link-colors.scssc +0 -0
- data/spec/internal/tmp/cache/sass/d1da036a47062a9f69ce0327962c00aa77d2ea44/_unstyled-link.scssc +0 -0
- data/spec/internal/tmp/cache/sass/d559b7bf0c5e0c0d23f2f34bfc21b4817fb38463/_links.scssc +0 -0
- data/spec/internal/tmp/cache/sass/d559b7bf0c5e0c0d23f2f34bfc21b4817fb38463/_lists.scssc +0 -0
- data/spec/internal/tmp/cache/sass/d559b7bf0c5e0c0d23f2f34bfc21b4817fb38463/_text.scssc +0 -0
- data/spec/internal/tmp/cache/sass/d559b7bf0c5e0c0d23f2f34bfc21b4817fb38463/_vertical_rhythm.scssc +0 -0
- data/spec/internal/tmp/cache/sass/e2e5b9bb57a9d1b018b9f546bc8b16f6b2a627a9/_compass.scssc +0 -0
- data/spec/internal/tmp/cache/sass/e654955d4502954a409bf21071c0d98eb4ac2e9f/_utilities.scssc +0 -0
- data/spec/javascripts/UploadSpec.js +75 -0
- data/spec/javascripts/helpers/SpecHelper.js +9 -0
- data/spec/javascripts/support/jasmine.yml +84 -0
- data/spec/unit/upload_controller_spec.rb +31 -0
- data/spec/{requests/uploader_spec.rb → unit/upload_spec.rb} +9 -10
- data/spec/unit/uploader_module_spec.rb +31 -0
- data/vendor/assets/javascripts/s3_multipart/lib.js +456 -0
- data/vendor/assets/javascripts/s3_multipart/lib.min.js +1 -0
- metadata +103 -12
- data/.rspec +0 -1
- data/lib/s3_multipart/uploader/config.rb +0 -15
- data/spec/internal/app/assets/stylesheets/application.css +0 -0
- data/spec/internal/db/combustion_test.sqlite3 +0 -0
- data/vendor/assets/javascripts/s3_multipart/index.js +0 -1
- data/vendor/assets/javascripts/s3_multipart/s3_multipart.js +0 -478
@@ -1,478 +0,0 @@
|
|
1
|
-
(function(global) {
|
2
|
-
global.S3MP = (function() {
|
3
|
-
|
4
|
-
// Wrap this into underscore library extension
|
5
|
-
_.mixin({
|
6
|
-
findIndex : function (collection, filter) {
|
7
|
-
for (var i = 0; i < collection.length; i++) {
|
8
|
-
if (filter(collection[i], i, collection)) {
|
9
|
-
return i;
|
10
|
-
}
|
11
|
-
}
|
12
|
-
return -1;
|
13
|
-
}
|
14
|
-
});
|
15
|
-
|
16
|
-
// S3MP Constructor
|
17
|
-
function S3MP(options) {
|
18
|
-
var files
|
19
|
-
, progress_timer = []
|
20
|
-
, S3MP = this;
|
21
|
-
|
22
|
-
// User defined options + callbacks
|
23
|
-
options = options || {
|
24
|
-
fileSelector: null,
|
25
|
-
bucket: null,
|
26
|
-
onStart: function(upload) {
|
27
|
-
console.log("File "+upload.num+" has started uploading")
|
28
|
-
},
|
29
|
-
onComplete: function(upload) {
|
30
|
-
console.log("File "+upload.num+" successfully uploaded")
|
31
|
-
},
|
32
|
-
onPause: function(num) {
|
33
|
-
console.log("File "+num+" has been paused")
|
34
|
-
},
|
35
|
-
onCancel: function(num) {
|
36
|
-
console.log("File upload "+num+" was canceled")
|
37
|
-
},
|
38
|
-
onError: function(num) {
|
39
|
-
console.log("There was an error")
|
40
|
-
},
|
41
|
-
onProgress: function(num, size, done, percent, speed) {
|
42
|
-
console.log("File %d is %f percent done (%f of %f total) and uploading at %s", num, percent, done, size, speed);
|
43
|
-
}
|
44
|
-
}
|
45
|
-
_.extend(this, options);
|
46
|
-
|
47
|
-
this.uploadList = [];
|
48
|
-
|
49
|
-
// Handles all of the user input, success/failure events, and
|
50
|
-
// progress notifiers
|
51
|
-
this.handler = {
|
52
|
-
|
53
|
-
// utility function to return an upload object given a file
|
54
|
-
_returnUploadObj: function(file) {
|
55
|
-
var uploadObj = _.find(S3MP.fileList, function(uploadObj) {
|
56
|
-
return uploadObj.file === file;
|
57
|
-
});
|
58
|
-
return uploadObj;
|
59
|
-
},
|
60
|
-
|
61
|
-
// Activate an appropriate number of parts (number = pipes)
|
62
|
-
// when all of the parts have been successfully initialized
|
63
|
-
beginUpload: function() {
|
64
|
-
var i = [];
|
65
|
-
function beginUpload(pipes, uploadObj) {
|
66
|
-
var key = uploadObj.key
|
67
|
-
, num_parts = uploadObj.parts.length
|
68
|
-
|
69
|
-
if (typeof i[key] === "undefined") {
|
70
|
-
i[key] = 0;
|
71
|
-
}
|
72
|
-
|
73
|
-
i[key]++;
|
74
|
-
|
75
|
-
if (i[key] === num_parts) {
|
76
|
-
for (var j=0; j<pipes; j++) {
|
77
|
-
uploadObj.parts[j].activate();
|
78
|
-
}
|
79
|
-
S3MP.handler.startProgressTimer(key);
|
80
|
-
S3MP.onStart(uploadObj); // This probably needs to go somewhere else.
|
81
|
-
}
|
82
|
-
}
|
83
|
-
return beginUpload;
|
84
|
-
}(),
|
85
|
-
|
86
|
-
// cancel a given file upload
|
87
|
-
cancel: function(file) {
|
88
|
-
var uploadObj, i;
|
89
|
-
|
90
|
-
uploadObj = this._returnUploadObj(file);
|
91
|
-
i = _.indexOf(S3MP.uploadList, uploadObj);
|
92
|
-
|
93
|
-
S3MP.uploadList.splice(i,i+1);
|
94
|
-
S3MP.onCancel();
|
95
|
-
},
|
96
|
-
|
97
|
-
// pause a given file upload
|
98
|
-
pause: function(file) {
|
99
|
-
var uploadObj = this._returnUploadObj(file);
|
100
|
-
|
101
|
-
_.each(uploadObj.activeParts, function(part, key, list) {
|
102
|
-
part.xhr.abort();
|
103
|
-
});
|
104
|
-
|
105
|
-
S3MP.onPause();
|
106
|
-
},
|
107
|
-
|
108
|
-
// called when an upload is paused or the network connection cuts out
|
109
|
-
onError: function(uploadObj, part) {
|
110
|
-
// To-do
|
111
|
-
},
|
112
|
-
|
113
|
-
// called when a single part has successfully uploaded
|
114
|
-
onPartSuccess: function(uploadObj, finished_part) {
|
115
|
-
var parts, i, ETag;
|
116
|
-
|
117
|
-
parts = uploadObj.parts;
|
118
|
-
|
119
|
-
// Append the ETag (in the response header) to the ETags array
|
120
|
-
ETag = finished_part.xhr.getResponseHeader("ETag");
|
121
|
-
uploadObj.Etags.push({ ETag: ETag.replace(/\"/g, ''), partNum: finished_part.num });
|
122
|
-
|
123
|
-
// Increase the uploaded count and delete the finished part
|
124
|
-
uploadObj.uploaded += finished_part.size;
|
125
|
-
uploadObj.inprogress[finished_part.num-1] = 0;
|
126
|
-
i = _.indexOf(parts, finished_part);
|
127
|
-
parts.splice(i,1);
|
128
|
-
|
129
|
-
// activate one of the remaining parts
|
130
|
-
if (parts.length) {
|
131
|
-
i = _.findIndex(parts, function(el, index, collection) {
|
132
|
-
if (el.status !== "active") {
|
133
|
-
return true;
|
134
|
-
}
|
135
|
-
});
|
136
|
-
if (i !== -1){
|
137
|
-
parts[i].activate();
|
138
|
-
}
|
139
|
-
}
|
140
|
-
|
141
|
-
// If no parts remain then the upload has finished
|
142
|
-
if (!parts.length) {
|
143
|
-
this.onComplete(uploadObj);
|
144
|
-
}
|
145
|
-
},
|
146
|
-
|
147
|
-
// called when all parts have successfully uploaded
|
148
|
-
onComplete: function(uploadObj) {
|
149
|
-
var key = _.indexOf(S3MP.uploadList, uploadObj);
|
150
|
-
|
151
|
-
// Stop the onprogress timer
|
152
|
-
this.clearProgressTimer(key);
|
153
|
-
|
154
|
-
// Tell the server to put together the pieces
|
155
|
-
S3MP.completeMultipart(uploadObj, function(obj) {
|
156
|
-
// Notify the client that the upload has succeeded when we
|
157
|
-
// get confirmation from the server
|
158
|
-
if (obj.location) {
|
159
|
-
S3MP.onComplete(uploadObj);
|
160
|
-
}
|
161
|
-
});
|
162
|
-
|
163
|
-
},
|
164
|
-
|
165
|
-
// Called by progress_timer
|
166
|
-
onProgress: function(key, size, done, percent, speed) {
|
167
|
-
S3MP.onProgress(key, size, done, percent, speed);
|
168
|
-
},
|
169
|
-
|
170
|
-
startProgressTimer: function() {
|
171
|
-
var last_upload_chunk = [];
|
172
|
-
var fn = function(key) {
|
173
|
-
progress_timer[key] = global.setInterval(function() {
|
174
|
-
var upload, size, done, percent, speed;
|
175
|
-
|
176
|
-
if (typeof last_upload_chunk[key] === "undefined") {
|
177
|
-
last_upload_chunk[key] = 0;
|
178
|
-
}
|
179
|
-
|
180
|
-
upload = S3MP.uploadList[key];
|
181
|
-
size = upload.size;
|
182
|
-
done = upload.uploaded;
|
183
|
-
|
184
|
-
_.each(upload.inprogress,function(val) {
|
185
|
-
done += val;
|
186
|
-
});
|
187
|
-
|
188
|
-
percent = done/size * 100;
|
189
|
-
speed = done - last_upload_chunk[key];
|
190
|
-
last_upload_chunk[key] = done;
|
191
|
-
|
192
|
-
upload.handler.onProgress(key, size, done, percent, speed);
|
193
|
-
}, 1000);
|
194
|
-
};
|
195
|
-
return fn;
|
196
|
-
}(),
|
197
|
-
|
198
|
-
clearProgressTimer: function(key) {
|
199
|
-
global.clearInterval(progress_timer[key]);
|
200
|
-
}
|
201
|
-
|
202
|
-
};
|
203
|
-
|
204
|
-
files = $(this.fileSelector).get(0).files;
|
205
|
-
|
206
|
-
_.each(files, function(file, key) {
|
207
|
-
//Do validation for each file before creating a new upload object
|
208
|
-
// if (!file.type.match(/video/)) {
|
209
|
-
// return false;
|
210
|
-
// }
|
211
|
-
if (file.size < 5000000) {
|
212
|
-
return false;
|
213
|
-
}
|
214
|
-
|
215
|
-
var upload = new Upload(file, S3MP, key);
|
216
|
-
S3MP.uploadList.push(upload);
|
217
|
-
upload.init();
|
218
|
-
});
|
219
|
-
|
220
|
-
};
|
221
|
-
|
222
|
-
// Upload constructor
|
223
|
-
function Upload(file, o, key) {
|
224
|
-
function Upload() {
|
225
|
-
var upload, id, parts, part, segs, chunk_segs, chunk_lens, pipes, blob;
|
226
|
-
|
227
|
-
upload = this;
|
228
|
-
|
229
|
-
this.key = key;
|
230
|
-
this.file = file;
|
231
|
-
this.name = file.name;
|
232
|
-
this.size = file.size;
|
233
|
-
this.type = file.type;
|
234
|
-
this.Etags = [];
|
235
|
-
this.inprogress = [];
|
236
|
-
this.uploaded = 0;
|
237
|
-
this.status = "";
|
238
|
-
|
239
|
-
// Break the file into an appropriate amount of chunks
|
240
|
-
// This needs to be optimized for various browsers types/versions
|
241
|
-
if (this.size > 1000000000) { // size greater than 1gb
|
242
|
-
num_segs = 100;
|
243
|
-
pipes = 10;
|
244
|
-
} else if (this.size > 500000000) { // greater than 500mb
|
245
|
-
num_segs = 50;
|
246
|
-
pipes = 5;
|
247
|
-
} else if (this.size > 100000000) { // greater than 100 mb
|
248
|
-
num_segs = 20;
|
249
|
-
pipes = 5;
|
250
|
-
} else if (this.size > 10000000) { // greater than 10 mb
|
251
|
-
num_segs = 2;
|
252
|
-
pipes = 2;
|
253
|
-
} else { // Do not use multi-part uploader
|
254
|
-
num_segs = 10;
|
255
|
-
pipes = 3;
|
256
|
-
}
|
257
|
-
|
258
|
-
chunk_segs = _.range(num_segs + 1);
|
259
|
-
chunk_lens = _.map(chunk_segs, function(seg) {
|
260
|
-
return Math.round(seg * (file.size/num_segs));
|
261
|
-
});
|
262
|
-
|
263
|
-
this.parts = _.map(chunk_lens, function(len, i) {
|
264
|
-
blob = upload.sliceBlob(file, len, chunk_lens[i+1]);
|
265
|
-
return new UploadPart(blob, i+1, upload);
|
266
|
-
});
|
267
|
-
|
268
|
-
this.parts.pop(); // Remove the empty blob at the end of the array
|
269
|
-
|
270
|
-
// init function will initiate the multipart upload, sign all the parts, and
|
271
|
-
// start uploading some parts in parallel
|
272
|
-
this.init = function() {
|
273
|
-
upload.initiateMultipart(upload, function(obj) {
|
274
|
-
console.log('Multipart initiation successful');
|
275
|
-
var id = upload.id = obj.id
|
276
|
-
, upload_id = upload.upload_id = obj.upload_id
|
277
|
-
, object_name = upload.object_name = obj.name
|
278
|
-
, parts = upload.parts;
|
279
|
-
|
280
|
-
upload.signPartRequests(id, object_name, upload_id, parts, function(response) {
|
281
|
-
_.each(parts, function(part, key) {
|
282
|
-
var xhr = part.xhr;
|
283
|
-
|
284
|
-
xhr.open('PUT', 'http://'+upload.bucket+'.s3.amazonaws.com/'+object_name+'?partNumber='+part.num+'&uploadId='+upload_id, true);
|
285
|
-
xhr.setRequestHeader('x-amz-date', response[key].date);
|
286
|
-
xhr.setRequestHeader('Authorization', response[key].authorization);
|
287
|
-
|
288
|
-
// Notify handler that an xhr request has been opened
|
289
|
-
upload.handler.beginUpload(pipes, upload);
|
290
|
-
});
|
291
|
-
});
|
292
|
-
});
|
293
|
-
}
|
294
|
-
};
|
295
|
-
// Inherit the properties and prototype methods of the S3MP instance object
|
296
|
-
Upload.prototype = o;
|
297
|
-
return new Upload();
|
298
|
-
}
|
299
|
-
|
300
|
-
// Upload part constructor
|
301
|
-
function UploadPart(blob, key, upload) {
|
302
|
-
var part, xhr;
|
303
|
-
|
304
|
-
part = this;
|
305
|
-
|
306
|
-
this.size = blob.size;
|
307
|
-
this.blob = blob;
|
308
|
-
this.num = key;
|
309
|
-
|
310
|
-
this.xhr = xhr = upload.createXhrRequest();
|
311
|
-
xhr.onload = function() {
|
312
|
-
upload.handler.onPartSuccess(upload, part);
|
313
|
-
};
|
314
|
-
xhr.onerror = function() {
|
315
|
-
upload.handler.onError(upload, part);
|
316
|
-
};
|
317
|
-
xhr.upload.onprogress = _.throttle(function(e) {
|
318
|
-
upload.inprogress[key] = e.loaded;
|
319
|
-
}, 1000);
|
320
|
-
|
321
|
-
};
|
322
|
-
|
323
|
-
UploadPart.prototype.activate = function() {
|
324
|
-
this.status = "active";
|
325
|
-
this.xhr.send(this.blob);
|
326
|
-
};
|
327
|
-
|
328
|
-
S3MP.prototype.initiateMultipart = function(upload, cb) {
|
329
|
-
var url, body, request, response;
|
330
|
-
|
331
|
-
url = '/s3_multipart/uploads';
|
332
|
-
body = JSON.stringify({ object_name : upload.name,
|
333
|
-
content_type : upload.type
|
334
|
-
});
|
335
|
-
|
336
|
-
request = this.createXhrRequest('POST', url, function(xhr) {
|
337
|
-
if (this.readyState !== 4) {
|
338
|
-
return false;
|
339
|
-
}
|
340
|
-
if (this.status !== 200) {
|
341
|
-
throw {
|
342
|
-
name: "ServerResponse",
|
343
|
-
message: "The server responded with an error"
|
344
|
-
};
|
345
|
-
}
|
346
|
-
|
347
|
-
response = JSON.parse(this.responseText);
|
348
|
-
cb(response);
|
349
|
-
});
|
350
|
-
|
351
|
-
request.setRequestHeader('Content-Type', 'application/json');
|
352
|
-
request.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'));
|
353
|
-
|
354
|
-
request.send(body);
|
355
|
-
|
356
|
-
};
|
357
|
-
|
358
|
-
S3MP.prototype.signPartRequests = function(id, object_name, upload_id, parts, cb) {
|
359
|
-
var content_lengths, url, body, request, response;
|
360
|
-
|
361
|
-
content_lengths = _.reduce(_.rest(parts), function(memo, part) {
|
362
|
-
return memo + "-" + part.size;
|
363
|
-
}, parts[0].size);
|
364
|
-
|
365
|
-
url = "s3_multipart/uploads/"+id;
|
366
|
-
body = JSON.stringify({ object_name : object_name,
|
367
|
-
upload_id : upload_id,
|
368
|
-
content_lengths : content_lengths
|
369
|
-
});
|
370
|
-
|
371
|
-
request = this.createXhrRequest('PUT', url, function(xhr) {
|
372
|
-
if (this.readyState !== 4) {
|
373
|
-
// Retry this chunk and give an error message
|
374
|
-
return false;
|
375
|
-
}
|
376
|
-
if (this.status !== 200) {
|
377
|
-
// Retry this chunk and give an error message
|
378
|
-
return false;
|
379
|
-
}
|
380
|
-
|
381
|
-
response = JSON.parse(this.responseText);
|
382
|
-
cb(response);
|
383
|
-
});
|
384
|
-
|
385
|
-
request.setRequestHeader('Content-Type', 'application/json');
|
386
|
-
request.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'));
|
387
|
-
|
388
|
-
request.send(body);
|
389
|
-
|
390
|
-
};
|
391
|
-
|
392
|
-
S3MP.prototype.completeMultipart = function(uploadObj, cb) {
|
393
|
-
var url, body, request, response;
|
394
|
-
|
395
|
-
url = 's3_multipart/uploads/'+uploadObj.id;
|
396
|
-
body = JSON.stringify({ object_name : uploadObj.object_name,
|
397
|
-
upload_id : uploadObj.upload_id,
|
398
|
-
content_length : uploadObj.size,
|
399
|
-
parts : uploadObj.Etags
|
400
|
-
});
|
401
|
-
|
402
|
-
request = this.createXhrRequest('PUT', url, function(xhr) {
|
403
|
-
if (this.readyState !== 4) {
|
404
|
-
// Retry this chunk and give an error message
|
405
|
-
return false;
|
406
|
-
}
|
407
|
-
if (this.status !== 200) {
|
408
|
-
// Retry this chunk and give an error message
|
409
|
-
return false;
|
410
|
-
}
|
411
|
-
|
412
|
-
response = JSON.parse(this.responseText);
|
413
|
-
cb(response);
|
414
|
-
})
|
415
|
-
|
416
|
-
request.setRequestHeader('Content-Type', 'application/json');
|
417
|
-
request.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'));
|
418
|
-
|
419
|
-
request.send(body);
|
420
|
-
};
|
421
|
-
|
422
|
-
S3MP.prototype.createXhrRequest = function() {
|
423
|
-
var xhrRequest;
|
424
|
-
|
425
|
-
// Sniff for xhr object
|
426
|
-
if (typeof XMLHttpRequest.constructor === "function") {
|
427
|
-
xhrRequest = XMLHttpRequest;
|
428
|
-
} else if (typeof XDomainRequest !== "undefined") {
|
429
|
-
xhrRequest = XDomainRequest;
|
430
|
-
} else {
|
431
|
-
xhrRequest = null; // Error out to the client
|
432
|
-
}
|
433
|
-
|
434
|
-
return function(method, url, cb, open) { // open defaults to true
|
435
|
-
var args, xhr, open = true;
|
436
|
-
|
437
|
-
args = Array.prototype.slice.call(arguments);
|
438
|
-
if (typeof args[0] === "undefined") {
|
439
|
-
cb = null;
|
440
|
-
open = false;
|
441
|
-
}
|
442
|
-
|
443
|
-
xhr = new xhrRequest();
|
444
|
-
if (open) { // open the request unless specified otherwise
|
445
|
-
xhr.open(method, url, true);
|
446
|
-
}
|
447
|
-
xhr.onreadystatechange = cb;
|
448
|
-
|
449
|
-
return xhr;
|
450
|
-
};
|
451
|
-
|
452
|
-
}();
|
453
|
-
|
454
|
-
S3MP.prototype.sliceBlob = function() {
|
455
|
-
var test_blob = new Blob();
|
456
|
-
|
457
|
-
if (test_blob.slice) {
|
458
|
-
return function(blob, start, end) {
|
459
|
-
return blob.slice(start, end);
|
460
|
-
}
|
461
|
-
} else if (test_blob.mozSlice) {
|
462
|
-
return function(blob, start, end) {
|
463
|
-
return blob.mozSlice(start, end);
|
464
|
-
}
|
465
|
-
} else if (test_blob.webkitSlice) {
|
466
|
-
return function(blob, start, end) {
|
467
|
-
return blob.webkitSlice(start, end);
|
468
|
-
}
|
469
|
-
} else {
|
470
|
-
throw new Error("File API not supported");
|
471
|
-
}
|
472
|
-
}();
|
473
|
-
|
474
|
-
return S3MP;
|
475
|
-
|
476
|
-
}());
|
477
|
-
|
478
|
-
}(this));
|