Menu

[r459]: / swfupload / trunk / FlashDevelop / classes / SWFUpload.as  Maximize  Restore  History

Download this file

534 lines (446 with data), 23.0 kB

  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
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
/*
* Development note: This code needs a security audit. E.g. If someone sets the control id string as some javascript it
* could make funny things happen with the callbacks.
*
* */
// Allow all domain
import flash.net.FileReferenceList;
import flash.net.FileReference;
import flash.external.ExternalInterface;
class SWFUpload {
static function main()
{
_root.onEnterFrame = function() {
if (this.getBytesLoaded() / this.getBytesTotal() > 0.99) {
var SWFUpload:SWFUpload = new SWFUpload();
_root.onEnterFrame = function() { };
_root.stop();
}
};
}
var fileBrowser:FileReferenceList = new FileReferenceList();
var file_queue:Array = new Array(); // holds a list of all items that are to be uploaded.
var current_file_item:FileItem = null; // the item that is currently being uploaded.
var single_upload:Boolean = false; // Indicates whether a single file is being upload or the entire queue
var completed_uploads:Number = 0; // Tracks the uploads that have been completed (no errors, not cancelled, not too big)
var queued_uploads:Number = 0; // Tracks the FileItems that are waiting to be uploaded.
var file_reference_listener:Object; // Holds the callbacks used by the FileReference objects
// Callbacks
var flashReady_Callback:String;
var dialogCancelled_Callback:String;
var fileQueued_Callback:String;
var fileProgress_Callback:String;
var fileCancelled_Callback:String;
var fileComplete_Callback:String;
var queueComplete_Callback:String;
var queueStopped_Callback:String;
var error_Callback:String;
var debug_Callback:String;
// Values passed in from the HTML
var controlID:String;
var uploadTargetURL:String;
var uploadQueryString:String;
var fileTypes:String;
var fileTypesDescription:String;
var fileSizeLimit:Number;
var fileUploadLimit:Number = 0;
var fileQueueLimit:Number = 0;
var beginUploadOnQueue:Boolean;
var debug_enabled:Boolean;
// Error code "constants"
var ERROR_CODE_HTTP_ERROR:Number = -10;
var ERROR_CODE_MISSING_UPLOAD_TARGET:Number = -20;
var ERROR_CODE_IO_ERROR:Number = -30;
var ERROR_CODE_SECURITY_ERROR:Number = -40;
var ERROR_CODE_FILE_EXCEEDS_SIZE_LIMIT:Number = -50;
var ERROR_CODE_ZERO_BYTE_FILE:Number = -60;
var ERROR_CODE_UPLOAD_LIMIT_EXCEEDED:Number = -70;
var ERROR_CODE_UPLOAD_FAILED:Number = -80;
var ERROR_CODE_QUEUE_LIMIT_EXCEEDED:Number = -90;
var ERROR_CODE_SPECIFIED_FILE_NOT_FOUND:Number = -100;
function SWFUpload() {
System.security.allowDomain("*"); // Allow any domain to use this SWF to upload files
// Setup file FileReference Listener. This is attached to all FileReference objects (ie, every file the user uploads)
this.file_reference_listener = new Object();
this.file_reference_listener.onCancel = Delegate.Create(this, this.DialogCancelled_Handler);
this.file_reference_listener.onProgress = Delegate.Create(this, this.FileProgress_Handler);
this.file_reference_listener.onComplete = Delegate.Create(this, this.FileComplete_Handler);
this.file_reference_listener.onHTTPError = Delegate.Create(this, this.HTTPError_Handler);
this.file_reference_listener.onIOError = Delegate.Create(this, this.IOError_Handler);
this.file_reference_listener.onSecurityError = Delegate.Create(this, this.SecurityError_Handler);
this.file_reference_listener.onSelect = Delegate.Create(this, this.Select_Handler);
// Add the event listener to the FileBrowser
this.fileBrowser.addListener(this.file_reference_listener);
// Get the control ID
this.controlID = _root.controlID;
// Configure the callbacks
this.flashReady_Callback = "SWFUpload.instances[\"" + this.controlID + "\"].flashReady";
this.dialogCancelled_Callback = "SWFUpload.instances[\"" + this.controlID + "\"].dialogCancelled";
this.fileQueued_Callback = "SWFUpload.instances[\"" + this.controlID + "\"].fileQueued";
this.fileProgress_Callback = "SWFUpload.instances[\"" + this.controlID + "\"].fileProgress";
this.fileCancelled_Callback = "SWFUpload.instances[\"" + this.controlID + "\"].fileCancelled";
this.fileComplete_Callback = "SWFUpload.instances[\"" + this.controlID + "\"].fileComplete";
this.queueComplete_Callback = "SWFUpload.instances[\"" + this.controlID + "\"].queueComplete";
this.queueStopped_Callback = "SWFUpload.instances[\"" + this.controlID + "\"].queueStopped";
this.error_Callback = "SWFUpload.instances[\"" + this.controlID + "\"].error";
this.debug_Callback = "SWFUpload.instances[\"" + this.controlID + "\"].debug";
// Get the Flash Vars
this.uploadTargetURL = _root.uploadTargetURL;
this.uploadQueryString = _root.uploadQueryString;
this.fileTypes = _root.fileTypes;
this.fileTypesDescription = _root.fileTypesDescription + " (" + this.fileTypes + ")";
try {
this.beginUploadOnQueue = _root.beginUploadOnQueue == "true" ? true : false;
} catch (ex:Object) {
this.beginUploadOnQueue = false;
}
try {
this.debug_enabled = _root.debug_enabled == "true" ? true : false;
} catch (ex:Object) {
this.debug_enabled = false;
}
try {
this.fileSizeLimit = Number(_root.fileSizeLimit);
} catch (ex:Object) {
this.fileSizeLimit = 0;
}
try {
this.fileUploadLimit = Number(_root.fileUploadLimit);
} catch (ex:Object) {
this.fileUploadLimit = 0;
}
try {
this.fileQueueLimit = Number(_root.fileQueueLimit);
} catch (ex:Object) {
this.fileQueueLimit = 0;
}
// There is no sense in allowing more files to be queued than is allowed to be uploaded
if (this.fileQueueLimit > this.fileUploadLimit) this.fileQueueLimit = this.fileUploadLimit;
var callbacks_set:Boolean = true;
callbacks_set = callbacks_set && ExternalInterface.addCallback("Browse", this, this.SelectFiles);
callbacks_set = callbacks_set && ExternalInterface.addCallback("StartUpload", this, this.StartUpload);
callbacks_set = callbacks_set && ExternalInterface.addCallback("StopUpload", this, this.StopUpload);
callbacks_set = callbacks_set && ExternalInterface.addCallback("CancelUpload", this, this.CancelUpload);
callbacks_set = callbacks_set && ExternalInterface.addCallback("CancelQueue", this, this.CancelQueue);
callbacks_set = callbacks_set && ExternalInterface.addCallback("SetUploadStrings", this, this.SetUploadStrings);
callbacks_set = callbacks_set && ExternalInterface.addCallback("AddFileParam", this, this.AddFileParam);
callbacks_set = callbacks_set && ExternalInterface.addCallback("RemoveFileParam", this, this.RemoveFileParam);
if (!callbacks_set) this.Debug("Callbacks where not set.");
this.Debug("SWFUpload Init Complete");
this.PrintDebugInfo();
/* This bit is the crux of "Flash Feature Detection" and should always be last. If anything before
this point fails (e.g. the flash player does not support it) then an error will occur which prevents
this method from calling back to the Javascript. Errors are not reported back to the browser
and so silently fails. Thus, the script will only work if the Flash Player supports the
necessary features. */
ExternalInterface.call(this.flashReady_Callback);
}
/* *****************************************
* FileReference Event Handlers
* *************************************** */
function DialogCancelled_Handler():Void {
this.Debug("onCancel: File Dialog window cancelled.");
ExternalInterface.call(this.dialogCancelled_Callback);
}
function FileProgress_Handler(file:FileReference, bytesLoaded:Number, bytesTotal:Number):Void {
this.Debug("onProgress: File ID: " + this.current_file_item.id + ". Bytes: " + bytesLoaded + ". Total: " + bytesTotal);
ExternalInterface.call(this.fileProgress_Callback, this.current_file_item.ToJavaScriptObject(), bytesLoaded, bytesTotal);
}
function FileComplete_Handler(file:FileReference):Void {
this.Debug("onComplete: File ID: " + this.current_file_item.id + ". Upload Complete. Calling uploadComplete.");
this.completed_uploads++;
ExternalInterface.call(this.fileComplete_Callback, this.current_file_item.ToJavaScriptObject());
this.UploadComplete();
}
function HTTPError_Handler(file:FileReference, httpError:Number):Void {
this.Debug("onHTTPError: File ID: " + this.current_file_item.id + ". HTTP Error: " + httpError + ".");
ExternalInterface.call(this.error_Callback, this.ERROR_CODE_HTTP_ERROR, this.current_file_item.ToJavaScriptObject(), httpError);
this.UploadComplete();
}
function IOError_Handler(file:FileReference):Void {
if(!this.uploadTargetURL.length) {
this.Debug("onIOError: File ID: " + this.current_file_item.id + ". Upload Backend string is empty.");
ExternalInterface.call(this.error_Callback, this.ERROR_CODE_MISSING_UPLOAD_TARGET, this.current_file_item.ToJavaScriptObject(), "No backend file specified");
} else {
this.Debug("onIOError: File ID: " + this.current_file_item.id + ". IO Error.");
ExternalInterface.call(this.error_Callback, this.ERROR_CODE_IO_ERROR, this.current_file_item.ToJavaScriptObject(), "IO Error");
}
this.UploadComplete();
}
function SecurityError_Handler(file:FileReference, errorString:String):Void {
this.Debug("onSecurityError: File Number: " + this.current_file_item.id + ". Error:" + errorString);
ExternalInterface.call(this.error_Callback, this.ERROR_CODE_SECURITY_ERROR, this.current_file_item.ToJavaScriptObject(), "Security Error");
this.UploadComplete();
}
function Select_Handler(fileRefList:FileReferenceList) {
this.Debug("Files Selected. Processing file list");
var file_reference_list:Array = fileRefList.fileList;
// Determine how many files may be queued
var queue_slots_remaining:Number = this.fileUploadLimit - (this.completed_uploads + this.queued_uploads);
queue_slots_remaining = (queue_slots_remaining > this.fileQueueLimit && this.fileQueueLimit > 0) ? this.fileQueueLimit : queue_slots_remaining;
// Check if the number of files selected is greater than the number allowed to queue up.
if (file_reference_list.length > queue_slots_remaining && (this.fileUploadLimit != 0 || this.fileQueueLimit > 0)) {
this.Debug("onSelect: Selected Files exceed remaining Queue size.");
ExternalInterface.call(this.error_Callback, this.ERROR_CODE_QUEUE_LIMIT_EXCEEDED, null, queue_slots_remaining);
} else {
// Process each selected file
for (var i:Number = 0; i < file_reference_list.length; i++) {
var file_item:FileItem = new FileItem(file_reference_list[i], this.controlID);
// Check the size, if it's within the limit add it to the upload list.
var size_result:Number = this.CheckFileSize(file_item);
if(size_result == 0) {
this.Debug("onSelect: File within size limit. Adding to queue and making callback.");
file_item.file_reference.addListener(this.file_reference_listener);
this.file_queue.push(file_item);
this.queued_uploads++;
ExternalInterface.call(this.fileQueued_Callback, file_item.ToJavaScriptObject());
// If it's too large then still reserve it's space (it needs a file id) and send the external notice
} else if (size_result > 0) {
this.Debug("onSelect: File exceeds size limit. Making error callback.");
ExternalInterface.call(this.error_Callback, this.ERROR_CODE_FILE_EXCEEDS_SIZE_LIMIT, file_item.ToJavaScriptObject(), "File size exceeds allowed limit.");
} else if (size_result < 0) {
this.Debug("onSelect: File is zero bytes. Making error callback.");
ExternalInterface.call(this.error_Callback, this.ERROR_CODE_ZERO_BYTE_FILE, file_item.ToJavaScriptObject(), "File is zero bytes and cannot be uploaded.");
}
}
// If we are currently uploading files
if (this.beginUploadOnQueue && this.queued_uploads > 0) {
this.Debug("onSelect: Uploads set to begin immediately. Calling StartUpload.");
this.StartUpload();
}
}
}
/* ****************************************************************
Externally exposed functions
****************************************************************** */
// Opens a file browser dialog. Once files are selected the "onselect" event is triggered.
function SelectFiles():Void {
var allowed_file_types:String = "*.*";
var allowed_file_types_description:String = "All Files";
if (this.fileTypes.length > 0) allowed_file_types = this.fileTypes;
if (this.fileTypesDescription.length > 0) allowed_file_types_description = this.fileTypesDescription;
this.fileBrowser.browse([{description: allowed_file_types_description, extension: allowed_file_types}]);
this.Debug("UploadFile: Browsing files. " + allowed_file_types);
}
// Starts uploading. If a file_id is given then only that file is uploaded, otherwise the entire queue is uploaded.
// If the file_id is not found no upload begins and an error is called back
function StartUpload(file_id:String):Void {
if (this.current_file_item == null) {
this.Debug("StartUpload(): Starting Upload. File ID: " + file_id);
this.StartFile(file_id);
} else {
this.Debug("StartUpload(): Upload run already in progress");
}
}
// Cancel the current upload and stops. Doesn't advance the upload pointer. Starting again will re-upload the current file.
function StopUpload():Void {
if (this.current_file_item != null) {
// Cancel the upload and re-queue the FileItem
this.current_file_item.file_reference.cancel();
this.file_queue.unshift(this.current_file_item);
ExternalInterface.call(this.queueStopped_Callback, this.current_file_item.ToJavaScriptObject());
this.current_file_item = null;
this.Debug("StopUpload(): upload stopped.");
} else {
this.Debug("StopUpload(): Upload run not in progress");
}
}
// Cancels the upload specified by file_id
function CancelUpload(file_id:String):Void {
if (file_id) {
if (this.current_file_item.id == file_id) {
this.current_file_item.file_reference.cancel();
ExternalInterface.call(this.fileCancelled_Callback, this.current_file_item.ToJavaScriptObject());
this.Debug("CancelUpload(): Cancelling current upload");
this.UploadComplete(); // <-- this advanced the upload to the next file
} else {
// Find the file in the queue
var file_index:Number = this.FindIndexInFileQueue(file_id);
if (file_index >= 0) {
// Remove the file from the queue
var file_item:FileItem = FileItem(this.file_queue[file_index]);
this.file_queue[file_index] = null;
this.queued_uploads--;
// Cancel the file (just for good measure) and make the callback
file_item.file_reference.cancel();
ExternalInterface.call(this.fileCancelled_Callback, file_item.ToJavaScriptObject());
// Get rid of the file object
delete file_item;
this.Debug("CancelUpload(): Cancelling queued upload");
}
}
}
}
function CancelQueue():Void {
this.Debug("CancelQueue(): Cancelling remaining queue.");
// Cancel the current upload
if (this.current_file_item != null) {
this.current_file_item.file_reference.cancel();
ExternalInterface.call(this.fileCancelled_Callback, this.current_file_item.ToJavaScriptObject());
}
while (this.file_queue.length > 0) {
var file_item:FileItem = FileItem(this.file_queue.shift());
if (file_item != null) {
file_item.file_reference.cancel(); // Cancel the FileReference just for good measure
this.queued_uploads--;
ExternalInterface.call(this.fileCancelled_Callback, file_item.ToJavaScriptObject());
delete file_item;
}
}
// If we were uploading a file complete it's process
if (this.current_file_item != null) {
this.UploadComplete();
}
}
function SetUploadStrings(url:String, query_string:String):Void {
this.uploadTargetURL = url;
this.uploadQueryString = query_string;
}
function AddFileParam(file_id:String, name:String, value:String):Boolean {
var file_index:Number = this.FindIndexInFileQueue(file_id);
if (file_index >= 0) {
var file_item:FileItem = FileItem(this.file_queue[file_index]);
file_item.AddParam(name, value);
return true;
} else {
return false;
}
}
function RemoveFileParam(file_id:String, name:String):Boolean {
var file_index:Number = this.FindIndexInFileQueue(file_id);
if (file_index >= 0) {
var file_item:FileItem = FileItem(this.file_queue[file_index]);
file_item.RemoveParam(name);
return true;
} else {
return false;
}
}
/* *************************************************************
File processing and handling functions
*************************************************************** */
//
function StartFile(file_id:String):Void {
// Only upload a file uploads are being processed.
// startFile could be called by a file cancellation even when we aren't currently uploading
if (this.current_file_item != null) {
this.Debug("StartFile(): Upload already in progress. Exiting.");
return;
}
this.Debug("StartFile: File ID " + file_id);
// Get the next file to upload
if (!file_id) {
this.single_upload = false;
while (this.file_queue.length > 0 && this.current_file_item == null) {
// Check that File Reference is valid (if not make sure it's deleted and get the next one on the next loop)
this.current_file_item = FileItem(this.file_queue.shift()); // Cast back to a FileItem
if (typeof(this.current_file_item) == "undefined") {
this.current_file_item = null;
continue;
}
// Check that we haven't exceeded the upload_limit, if so handle the file and go to the next.
else if (completed_uploads >= this.fileUploadLimit && this.fileUploadLimit != 0) {
this.Debug("StartFile(): Upload limit reached. Making callback and skipping file. File ID: " + this.current_file_item.id);
ExternalInterface.call(this.error_Callback, this.ERROR_CODE_UPLOAD_LIMIT_EXCEEDED, this.current_file_item.ToJavaScriptObject(), "The upload limit has been reached.");
delete this.current_file_item;
this.current_file_item = null;
continue;
}
}
} else {
this.single_upload = true;
var file_index:Number = this.FindIndexInFileQueue(file_id);
if (file_index >= 0) {
this.current_file_item = FileItem(this.file_queue[file_index]);
this.file_queue[file_index] = null;
} else {
this.Debug("StartFile(): File ID not found in queue: " + file_id);
ExternalInterface.call(this.error_Callback, this.ERROR_CODE_SPECIFIED_FILE_NOT_FOUND, null, "File ID not queued.");
}
}
// Start the upload if we found an item to upload and we haven't passed the upload limit
if (this.current_file_item != null) {
// Build the query string
var file_query_string:String = this.current_file_item.GetQueryString();
var query_string_array:Array = new Array();
if (this.uploadQueryString != "") query_string_array.push(this.uploadQueryString);
if (file_query_string != "") query_string_array.push(file_query_string);
var query_string:String = query_string_array.join("&");
// Combine the full URL
var upload_url:String = this.uploadTargetURL + (query_string != "" ? "?" : "") + query_string;
// Begin the upload
this.Debug("startFile(): File Reference found. Starting upload to " + upload_url + ". File ID: " + this.current_file_item.id);
if (this.current_file_item.file_reference.upload(upload_url)) {
this.Debug("startFile(): Upload started.");
} else {
this.Debug("startFile(): Upload Failed.");
ExternalInterface.call(this.error_Callback, this.ERROR_CODE_UPLOAD_FAILED, this.current_file_item.ToJavaScriptObject(), "Upload Failed.");
delete this.current_file_item;
this.current_file_item = null;
}
}
// Otherwise we've would have looped through all the FileItems. This means the queue is empty)
else {
this.Debug("startFile(): No File Reference found. All uploads must be complete, cancelled, or errored out.");
this.Debug("startFile(): Ending upload run. Completed Uploads: " + this.completed_uploads);
if (this.completed_uploads > 0) {
this.Debug("startFile(): Queue is complete and at least one file has been uploaded. Making QueueComplete callback.");
ExternalInterface.call(this.queueComplete_Callback, this.completed_uploads);
}
}
}
// Completes the file upload by deleting it's reference, advancing the pointer, and starting the next upload.
function UploadComplete() {
this.Debug("UploadComplete(): Upload complete. Removing File Reference and starting the next upload.");
// Remove the pointer, the space in the array still exists
delete this.current_file_item;
this.current_file_item = null;
this.queued_uploads--;
// Start the next file upload
if (!this.single_upload) {
this.StartFile();
}
}
/* *************************************************************
Utility Functions
*************************************************************** */
// Check the size of the file against the allowed file size. If it is less the return TRUE. If it is too large return FALSE
function CheckFileSize(file_item:FileItem):Number {
if (file_item.file_reference.size == 0) {
return -1;
} else if (this.fileSizeLimit != 0 && file_item.file_reference.size > (this.fileSizeLimit * 1000)) {
return 1;
} else {
return 0;
}
}
function Debug(msg:String):Void {
if (this.debug_enabled) {
var lines:Array = msg.split("\n");
for (var i:Number=0; i < lines.length; i++) {
lines[i] = "SWF DEBUG: " + lines[i];
}
ExternalInterface.call(this.debug_Callback, lines.join("\n"));
}
}
function PrintDebugInfo():Void {
var debug_info:String = "\n----- SWF DEBUG OUTPUT ----\n";
debug_info += "ControlID: " + this.controlID + "\n";
debug_info += "Upload Target URL: " + this.uploadTargetURL + "\n";
debug_info += "Upload Query String: " + this.uploadQueryString + "\n";
debug_info += "Begin Upload on Queue: " + this.beginUploadOnQueue + "\n";
debug_info += "File Types: " + this.fileTypes + "\n";
debug_info += "File Types Description: " + this.fileTypesDescription + "\n";
debug_info += "File Size Limit: " + this.fileSizeLimit + "\n";
debug_info += "File Upload Limit: " + this.fileUploadLimit + "\n";
debug_info += "File Queue Limit: " + this.fileQueueLimit + "\n";
debug_info += "----- END SWF DEBUG OUTPUT ----\n";
this.Debug(debug_info);
}
function FindIndexInFileQueue(file_id:String):Number {
for (var i:Number = 0; i<this.file_queue.length; i++) {
if (FileItem(this.file_queue[i]).id == file_id) return i;
}
return null;
}
}
Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.