Skip to content

Commit cc4c963

Browse files
committed
Merge pull request #57 from wingedfox/master
Resumable upload
2 parents 708ca53 + 167ae7c commit cc4c963

File tree

6 files changed

+147
-46
lines changed

6 files changed

+147
-46
lines changed

FileAPI.min.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# FileAPI — a set of tools for working with files.
1+
# FileAPI — a set of tools for working with files.
22

33

44
<p align="center">
@@ -220,7 +220,7 @@ function onDrop(evt){
220220

221221
var el = document.getElementById('el');
222222
FileAPI.event.dnd(el, function (over/**Boolean*/, evt/**Event*/){
223-
el.style.background = ever ? 'red' : '';
223+
el.style.background = over ? 'red' : '';
224224
}, function (files/**Array*/, evt/**Event*/){
225225
// ...
226226
});

lib/FileAPI.Flash.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/**
1+
/**
22
* FileAPI fallback to Flash
33
*
44
* @flash-developer "Vladimer Demidov" <[email protected]>

lib/FileAPI.Form.js

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
}
2727
},
2828

29-
toData: function (fn){
29+
toData: function (fn, options){
3030
if( !api.support.html5 ){
3131
api.log('FileAPI.Form.toHtmlData');
3232
this.toHtmlData(fn);
@@ -35,6 +35,10 @@
3535
api.log('FileAPI.Form.toMultipartData');
3636
this.toMultipartData(fn);
3737
}
38+
else if( api.support.chunked && options.chunkSize > 0 ){
39+
api.log('FileAPI.Form.toMultipartData');
40+
this.toPlainData(fn);
41+
}
3842
else {
3943
api.log('FileAPI.Form.toFormData');
4044
this.toFormData(fn);
@@ -74,6 +78,19 @@
7478
});
7579
},
7680

81+
toPlainData: function (fn){
82+
this._to({}, fn, function (file, data, queue){
83+
if( file.file ){
84+
data.type = file.file;
85+
}
86+
data.name = file.blob.name;
87+
data.file = file.blob;
88+
data.size = file.blob.size;
89+
data.start = 0;
90+
data.end = 0;
91+
data.retry = 0;
92+
});
93+
},
7794

7895
toFormData: function (fn){
7996
this._to(new FormData, fn, function (file, data, queue){

lib/FileAPI.XHR.js

Lines changed: 103 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@
6565
// Start uploading
6666
options.upload(options, _this);
6767
_this._send.call(_this, options, data);
68-
});
68+
}, options);
6969
},
7070

7171
_send: function (options, data){
@@ -77,6 +77,7 @@
7777
url += (~url.indexOf('?') ? '&' : '?') + api.uid();
7878

7979
if( data.nodeName ){
80+
// legacy
8081
options.upload(options, _this);
8182

8283
xhr = document.createElement('div');
@@ -125,6 +126,7 @@
125126
form = null;
126127
}
127128
else {
129+
// html5
128130
xhr = _this.xhr = api.getXHR();
129131

130132
xhr.open('POST', url, true);
@@ -138,46 +140,114 @@
138140
xhr.setRequestHeader(key, val);
139141
});
140142

141-
if( xhr.upload ){
142-
// https://github.com/blueimp/jQuery-File-Upload/wiki/Fixing-Safari-hanging-on-very-high-speed-connections-%281Gbps%29
143-
xhr.upload.addEventListener('progress', api.throttle(function (/**Event*/evt){
144-
options.progress(evt, _this, options);
145-
}, 100), false);
146-
}
147-
148-
xhr.onreadystatechange = function (){
149-
_this.status = xhr.status;
150-
_this.statusText = xhr.statusText;
151-
_this.readyState = xhr.readyState;
143+
144+
if (api.support.chunked && options.chunkSize > 0) {
145+
// resumable upload
146+
if( xhr.upload ){
147+
// https://github.com/blueimp/jQuery-File-Upload/wiki/Fixing-Safari-hanging-on-very-high-speed-connections-%281Gbps%29
148+
xhr.upload.addEventListener('progress', api.throttle(function (/**Event*/evt){
149+
var e = api.extend({}, evt, {
150+
loaded : data.start + evt.loaded,
151+
totalSize : data.size,
152+
total : data.size});
153+
options.progress(e, _this, options);
154+
}, 100), false);
155+
}
152156

153-
if( xhr.readyState == 4 ){
154-
for( var k in { '': 1, XML: 1, Text: 1, Body: 1 } ){
155-
_this['response'+k] = xhr['response'+k];
157+
xhr.onreadystatechange = function (){
158+
_this.status = xhr.status;
159+
_this.statusText = xhr.statusText;
160+
_this.readyState = xhr.readyState;
161+
162+
if( xhr.readyState == 4 ){
163+
for( var k in { '': 1, XML: 1, Text: 1, Body: 1 } ){
164+
_this['response'+k] = xhr['response'+k];
165+
}
166+
xhr.onreadystatechange = null;
167+
168+
if (xhr.status - 201 > 0) {
169+
// some kind of error
170+
if (++data.retry <= options.chunkUploadRetry && (500 == xhr.status || 416 == xhr.status)) {
171+
// let's try again the same chunk
172+
// only applicable for recoverable error codes 500 && 416
173+
data.end = data.start
174+
_this._send(options, data);
175+
} else {
176+
// no mo retries
177+
_this.end(xhr.status);
178+
}
179+
} else {
180+
// success
181+
data.retry = 0;
182+
183+
if (data.end == data.size - 1) {
184+
// finished
185+
_this.end(xhr.status);
186+
} else {
187+
// next chunk
188+
_this._send(options, data);
189+
}
190+
}
191+
xhr = null;
156192
}
157-
xhr.onreadystatechange = null;
158-
_this.end(xhr.status);
159-
xhr = null;
193+
};
194+
195+
data.start = data.end;
196+
data.end = Math.min(data.end + options.chunkSize, data.size ) - 1;
197+
198+
var slice;
199+
(slice = 'slice') in data.file || (slice = 'mozSlice') in data.file || (slice = 'webkitSlice') in data.file;
200+
201+
xhr.setRequestHeader("Content-Range", "bytes " + data.start + "-" + data.end + "/" + data.size);
202+
xhr.setRequestHeader("Content-Disposition", 'attachment; filename=' + data.name);
203+
204+
slice = data.file[slice](data.start, data.end + 1);
205+
206+
xhr.send(slice);
207+
slice = null;
208+
} else {
209+
// single piece upload
210+
if( xhr.upload ){
211+
// https://github.com/blueimp/jQuery-File-Upload/wiki/Fixing-Safari-hanging-on-very-high-speed-connections-%281Gbps%29
212+
xhr.upload.addEventListener('progress', api.throttle(function (/**Event*/evt){
213+
options.progress(evt, _this, options);
214+
}, 100), false);
160215
}
161-
};
216+
217+
xhr.onreadystatechange = function (){
218+
_this.status = xhr.status;
219+
_this.statusText = xhr.statusText;
220+
_this.readyState = xhr.readyState;
221+
222+
if( xhr.readyState == 4 ){
223+
for( var k in { '': 1, XML: 1, Text: 1, Body: 1 } ){
224+
_this['response'+k] = xhr['response'+k];
225+
}
226+
xhr.onreadystatechange = null;
227+
_this.end(xhr.status);
228+
xhr = null;
229+
}
230+
};
162231

163-
if( api.isArray(data) ){
164-
// multipart
165-
xhr.setRequestHeader('Content-Type', 'multipart/form-data; boundary=_'+api.expando);
166-
data = data.join('') +'--_'+ api.expando +'--';
232+
if( api.isArray(data) ){
233+
// multipart
234+
xhr.setRequestHeader('Content-Type', 'multipart/form-data; boundary=_'+api.expando);
235+
data = data.join('') +'--_'+ api.expando +'--';
167236

168-
/** @namespace xhr.sendAsBinary https://developer.mozilla.org/ru/XMLHttpRequest#Sending_binary_content */
169-
if( xhr.sendAsBinary ){
170-
xhr.sendAsBinary(data);
171-
}
172-
else {
173-
var bytes = Array.prototype.map.call(data, function(c){ return c.charCodeAt(0) & 0xff; });
174-
xhr.send(new Uint8Array(bytes).buffer);
237+
/** @namespace xhr.sendAsBinary https://developer.mozilla.org/ru/XMLHttpRequest#Sending_binary_content */
238+
if( xhr.sendAsBinary ){
239+
xhr.sendAsBinary(data);
240+
}
241+
else {
242+
var bytes = Array.prototype.map.call(data, function(c){ return c.charCodeAt(0) & 0xff; });
243+
xhr.send(new Uint8Array(bytes).buffer);
175244

245+
}
246+
} else {
247+
// FormData
248+
xhr.send(data);
176249
}
177250
}
178-
else {
179-
xhr.send(data);
180-
}
181251
}
182252
}
183253
};

lib/FileAPI.core.js

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
&& !(/safari\//.test(userAgent) && /windows/i.test(userAgent)), // BugFix: https://github.com/mailru/FileAPI/issues/25
2121

2222
cors = html5 && ('withCredentials' in (new XMLHttpRequest)),
23+
24+
chunked = html5 && !!Blob && !!(Blob.prototype.webkitSlice||Blob.prototype.mozSlice||Blob.prototype.slice),
2325

2426
document = window.document,
2527

@@ -35,8 +37,16 @@
3537
_rinput = /input/i,
3638
_rdata = /^data:[^,]+,/,
3739

38-
_KB = 1024,
3940
_pow = Math.pow,
41+
_round = Math.round,
42+
_num = Number,
43+
_from = function (sz) {
44+
return _round(sz * this);
45+
},
46+
_KB = new _num(1024),
47+
_MB = new _num(_pow(_KB, 2)),
48+
_GB = new _num(_pow(_KB, 3)),
49+
_TB = new _num(_pow(_KB, 4)),
4050

4151
_elEvents = {}, // element event listeners
4252
_infoReader = [], // list of file info processors
@@ -58,11 +68,14 @@
5868

5969
flashUrl: 0, // @default: './FileAPI.flash.swf'
6070
flashImageUrl: 0, // @default: './FileAPI.flash.image.swf'
71+
72+
chunkSize : 0,
73+
chunkUploadRetry : 0,
6174

62-
KB: _KB,
63-
MB: _pow(_KB, 2),
64-
GB: _pow(_KB, 3),
65-
TB: _pow(_KB, 4),
75+
KB: (_KB.from = _from, _KB),
76+
MB: (_MB.from = _from, _MB),
77+
GB: (_GB.from = _from, _GB),
78+
TB: (_TB.from = _from, _TB),
6679

6780
expando: 'fileapi' + (new Date).getTime(),
6881

@@ -103,6 +116,7 @@
103116
dnd: cors && ('ondrop' in document.createElement('div')),
104117
cors: cors,
105118
html5: html5,
119+
chunked: chunked,
106120
dataURI: true
107121
},
108122

@@ -712,9 +726,10 @@
712726
, filecomplete: api.F
713727
, progress: api.F
714728
, complete: api.F
729+
, chunkSize: api.chunkSize
730+
, chunkUpoloadRetry: api.chunkUploadRetry
715731
}, options);
716732

717-
718733
if( options.imageAutoOrientation && !options.imageTransform ){
719734
options.imageTransform = { rotate: 'auto' };
720735
}
@@ -747,7 +762,6 @@
747762

748763
// emit "beforeupload" event
749764
options.beforeupload(proxyXHR, options);
750-
751765
// Upload by file
752766
(function _nextFile(){
753767
var

0 commit comments

Comments
 (0)