-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcustomUploader.js
282 lines (236 loc) · 9.02 KB
/
customUploader.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
/*
* This is a very customized implementation of the jQuery File Upload plugin
* configured to work with nginx.
*/
$(function() {
var calculateProgress, cancelAllUploads, cancelUpload, createProgressBar,
fileName, files, maxChunkSize, startAllUploads, startUpload, uploadedFilePath;
// A container to hold all of the upload data objects.
files = [];
/*
* A simple method to calculate the progress for an individual file upload.
*/
calculateProgress = function(data) {
var value;
value = parseInt(data.loaded / data.total * 100, 10) || 0;
return value + "%";
};
/*
* Get the name of the file from the upload data.
*/
fileName = function(data) {
return data.files[0].name;
};
/*
* Returns the path to the uploaded file on the server.
*/
uploadedFilePath = function(data) {
var response;
response = JSON.parse(data.result);
if (response.files) {
return response.files[0][".path"];
} else {
return response[".path"];
}
};
/*
* Cancels the upload for a file found at 'index' in the 'files' container.
*/
cancelUpload = function(index) {
if (files[index]) {
files[index].jqXHR.abort();
}
};
/*
* Starts the upload for a file found at 'index' in the 'files' container.
* If the file upload was interrupted, the 'uploadedBytes' attribute will be
* reset to continue from where it left off.
*/
startUpload = function(index) {
var context, data;
data = files[index];
context = data.context;
data.uploadedBytes = parseInt($(context).attr("uploadedBytes"), 10);
data.data = null;
$(data).submit();
};
cancelAllUploads = function() {
$(files).each(function(index, file) {
cancelUpload(index);
});
};
startAllUploads = function() {
$(files).each(function(index, data) {
startUpload(index);
});
};
createProgressBar = function(progress) {
return '<span class="bar" style="width: ' + progress + '">' + progress + '</span>';
};
/*
* IMPORTANT: There are some very important settings to mention:
*
* maxChunkSize:
* Nginx and this plugin require a very specific setting for chunk sizes.
* Changing this may affect the performance and reliability of your upload.
*
* For a more in-depth explanation of the settings or the benchmarks performed
* to get this setting, read this document: https://gist.github.com/3920385.
*
* multipart:
* This must be set to 'false'. If set to 'true', then the upload will send
* in a single chunk, rather than multiple chunks.
*
* dataType:
* This cannot be set for chunked uploads! Setting this option to 'json'
* resulted in failed chunked uploads.
*
*/
$("#fileupload").fileupload({
maxChunkSize: 1024 * 512,
maxRetries: 15,
retryTimeout: 1000,
multipart: false,
add: function(e, data) {
// Collect some basic information about the file.
var progress = calculateProgress(data);
var filename = fileName(data);
// A count of the number of rows (current file uploads)
var index = $("#files tr").length;
// Create a start and stop button for this specific upload. The 'data-file'
// attribute is used to pass the index of this upload to the cancelUpload
// and startUpload methods.
var cancelButton = $('<button type="button" data-file="' + index + '">Cancel upload</button>');
var startButton = $('<button type="button" data-file="' + index + '">Start upload</button>');
// Cancel this specific upload when this button is clicked
cancelButton.click(function() {
cancelUpload($(this).attr("data-file"));
});
// Start/Resume this specific upload when this button is clicked
startButton.click(function() {
startUpload($(this).attr("data-file"));
});
// Create a new, empty row that will serve as the context for this file
// upload.
var row = $('<tr><td class="filename"></td><td class="progress"></td><td class="start"></td><td class="cancel"></td>');
// nginx requires us to specify a session id so that it can handle chunked
// uploads. Here, we're using the current time and the file's encoded name
// to generate this token.
//
// Note: This will require you to use the jQuery base64 plugin.
var sessionID = new Date().getTime() + '_'
+ $.base64.encode(filename).replace(/\+|=|\//g, '');
// Set all the information for this upload on the context (row) for easier
// access
$(row).find(".filename").text(filename);
$(row).find(".progress").html(createProgressBar(progress));
$(row).find(".start").append(startButton);
$(row).find(".cancel").append(cancelButton);
$(row).attr("sessionID", sessionID);
// Add the new file upload row to our list (table) of file uploads
$(row).appendTo("#files");
// Assign this row to this upload's context
data.context = row;
// Add this upload data to our files container
files.push(data);
},
/*
* Do something when the upload is done. This example replaces the progress
* bar we've been using with the path to the uploaded file on the server.
*/
done: function(e, data) {
data.context.find(".progress").html(uploadedFilePath(data));
},
/*
* This method is called whenever progress is reported back from nginx.
* Here, we're simply updating our progress bar to show the current progress.
* We're also clearing out any previous retry attempts once progress has
* been made.
*/
progress: function(e, data) {
var progress;
data.context.removeData("retries");
progress = calculateProgress(data);
data.context.find(".progress").html(createProgressBar(progress));
},
/*
* This callback keeps track of the combined progress for all active uploads.
*/
progressall: function (e, data) {
var progress = calculateProgress(data);
$("#total_progress").text(progress);
},
/*
* This method prepares the chunk that is about to be uploaded.
*/
beforeSend: function(e, files, index, xhr, handler, callback) {
var chrome, context, device, file, filename, filesize, ios, sessionID;
// Retrieve the file that is about to be sent to nginx
file = files.files[0];
// Collect some basic file information
filename = file.name;
filesize = file.size;
// Grab the context (table row) for this upload
context = files.context[0];
// Get the generated sessionID for this upload
sessionID = $(context).attr("sessionID");
// Set uploadedBytes on the context to ensure that if this upload was
// resumed, it will continue from where it left off.
$(context).attr("uploadedBytes", files.uploadedBytes);
// Set the required headers for the nginx upload module
e.setRequestHeader("Session-ID", sessionID);
e.setRequestHeader("X-Requested-With", "XMLHttpRequest");
device = navigator.userAgent.toLowerCase();
ios = device.match(/(iphone|ipod|ipad)/);
chrome = device.match(/crios/);
if (ios && !chrome) {
e.setRequestHeader("Cache-Control", "no-cache");
}
},
/*
* This method will be called whenever an upload (or a single chunk) fails
* to complete. In this case, we're setting up an auto-resume feature to
* attempt the upload again (respecting our retry and timeout settings).
*/
fail: function(e, data) {
var maxRetries, retryCount, retryTimeout, row;
// Get the context for this upload
row = $(data.context[0]);
// Grab its current retry count
retryCount = row.data("retries") || 1;
// Get our maxRetries and retryTimeout settings
maxRetries = $(this).data("fileupload").options.maxRetries + 1;
retryTimeout = $(this).data("fileupload").options.retryTimeout;
// If we can still attempt a retry
if (retryCount < maxRetries) {
window.setTimeout(function() {
// Set the row's progress bar section to display that we are trying again
row.find(".progress").html("<label>Retry #" + retryCount + "</label>");
// Increment the retry count and set it back on the row
row.data("retries", retryCount += 1);
// Reassign the uploadedBytes, then submit to start the upload again.
data.uploadedBytes = parseInt(row.attr("uploadedBytes"), 10);
data.data = null;
$(data).submit();
}, retryCount * retryTimeout);
} else {
// We've met our retry limit. Indicate that this upload has failed.
row.find(".progress").html("<label>Upload failed</label>");
}
}
});
/*
* A convenient method for triggering the upload of multiple files from the
* click of a button.
*/
$("#start_upload").click(function() {
startAllUploads();
});
/*
* A convenient method for triggering the cancellation of multiple files from
* the click of a button.
*/
$("#stop_uploads").click(function() {
cancelAllUploads();
});
});