Uploadify跨域上传原理

时间:2021-09-28 14:08:18

引用:http://www.cnblogs.com/me-sa/archive/2010/05/21/How-Uploadify-Cross-Domain.html

《 回头再说:jQuery跨域原理 》一文提到浏览器的同源策略以及使用JsonP的方式实现跨域;在评论中金色海洋提出了一个问题:

我最近在用 uploadify + ashx 来做文件上传的功能。都测试成功了,但是发现我可以提交到其他的网站里面。

我是在本地测试了。两个网站,IP地址相同,使用端口来区分。

一个端口是8001,另一个是8002 。

两个网站都有上传文件的程序,我发现,如果我把8001端口的站点的

'script': '/_CommonPage/UploadHandler.ashx',

改成

'script': 'http://192.168.0.1:8002/_CommonPage/UploadHandler.ashx',

居然也能够成功上传文件,传到了8002对应的网站里面。

我不知道如果换成域名了,是否也是可以往不同的域名里上传文件?

如果是的话,是不是很危险?如何来验证呢? 

 我给出的错误解释

         看到金色海洋的问题之后,我下载下来Uploadify源码看了一下,然后非常草率的给出了一个解释,点击这里;对于这个错误的解释,园友mx1700提出了质疑;于是下班回家之后,我开始动手模拟金色海洋的环境,试图一探究竟; 

Uploadify工作机制

       我在VS2010里面新建了一个webApplication,在页面上使用Uploadify,页面代码如下:   

Uploadify跨域上传原理Uploadify跨域上传原理
 1 <script src="Scripts/jquery-1.4.1.js" type="text/javascript"></script>
 2     <script src="Scripts/jquery.uploadify.v2.1.0.js" type="text/javascript"></script>
 3     <script src="Scripts/swfobject.js" type="text/javascript"></script>
 4     <script type="text/javascript">
 5        // <![CDATA[
 6         var id = "55";
 7         var theString = "asdf";
 8         $(document).ready(function () {
 9             $('#fileInput').uploadify({
10                 'uploader': 'uploadify.swf',
11                 'script': 'http://www.b.com:84/Uploader.ashx',
12                 'scriptData': { 'id': id, 'foo': theString },
13                 'cancelImg': 'cancel.png',
14                 'auto': true,
15                 'multi': true,
16                 'fileDesc': 'Image Files',
17                 'fileExt': '*.jpg;*.png;*.gif;*.bmp;*.jpeg',
18                 'queueSizeLimit': 90,
19                 'sizeLimit': 4000000,
20                 'buttonText': 'Choose Images',
21                 'folder': '/uploads',
22                 'onAllComplete': function (event, queueID, fileObj, response, data) {
23 
24                 }
25             });
26         });
27    // ]]></script>
28 
29    <input id="fileInput" name="fileInput" type="file" />
View Code

通过单步调试,我发现页面上会通过swfobject.js动态加载一个Flash,我们在页面上操作的实际上是Flash.  jQuery的作用是页面、Flash、目标服务器之间的黏合剂。它定义用户上传操作在不同时机的响应函数.而在这里 我所认为“发送数据上传文件”的函数,只不过是一个用来检查服务器上是不是已经存在同名文件的函数;由于这个检查有可能是跨域,所以 Uploadify间接使用了jQuery的JsonP跨域解决方案.

 

        通过排除,我们能有一个基本的判断:文件上传是在Flash里面完成的.那么Flash里面做了什么呢?还好有uploadify.fla文件,使用Flex Builder打开这个文件来看,果然如此,很多朋友没有Flex Builde,这里贴出完整代码;可以看到,

1
// Upload each file<br>function uploadFile(file:FileReference, index:int, ID:String, single:Boolean):

    实际的上传操作,而上传不同时机使用的很多函数都是定义在jquery.uploadify.v2.1.0.min.js文件里面.

Uploadify跨域上传原理Uploadify跨域上传原理
  1 uploadify.fla
  2 /*
  3 Uploadify v2.1.0
  4 Release Date: August 24, 2009
  5 
  6 Copyright (c) 2009 Ronnie Garcia, Travis Nickels
  7 
  8 Permission is hereby granted, free of charge, to any person obtaining a copy
  9 of this software and associated documentation files (the "Software"), to deal
 10 in the Software without restriction, including without limitation the rights
 11 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 12 copies of the Software, and to permit persons to whom the Software is
 13 furnished to do so, subject to the following conditions:
 14 
 15 The above copyright notice and this permission notice shall be included in
 16 all copies or substantial portions of the Software.
 17 
 18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 21 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 22 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 23 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 24 THE SOFTWARE.
 25  */
 26 
 27 import flash.external.ExternalInterface;
 28 import flash.net.*;
 29 import flash.events.*;
 30 import flash.display.*;
 31 import com.adobe.serialization.json.JSON;
 32 
 33  // Align the stage to the top left and don't scale it
 34  stage.align = StageAlign.TOP_LEFT;
 35 stage.scaleMode = StageScaleMode.NO_SCALE;
 36 
 37  // Create all the variables
 38  var param:Object = LoaderInfo(this.root.loaderInfo).parameters;
 39  var fileRefSingle:FileReference    = new FileReference();
 40  var fileRefMulti:FileReferenceList = new FileReferenceList();
 41  var fileRefListener:Object = new Object();
 42  var fileQueue:Array        = new Array();
 43  var fileItem:Object        = new Object();
 44  var activeUploads:Object   = new Object();
 45  var errorArray:Array       = new Array();
 46  var counter:Number        = 0;
 47  var filesSelected:Number  = 0;
 48  var filesReplaced:Number  = 0;
 49  var filesUploaded:Number  = 0;
 50  var filesChecked:Number   = 0;
 51  var errors:Number         = 0;
 52  var kbs:Number            = 0;
 53 var allBytesLoaded:Number = 0;
 54 var allBytesTotal:Number  = 0;
 55 var allKbsAvg:Number      = 0;
 56 var allowedTypes:Array;
 57 var scriptURL:URLRequest;
 58 var variables:URLVariables;
 59 var queueReversed:Boolean = false;
 60 
 61 // For debugging, alert any value to javascript
 62 function debug(someValue) {
 63     ExternalInterface.call('alert("' + someValue + '")');
 64 }
 65 
 66 // Trigger a javascript event
 67 function $trigger(eventName:String, ... args):void {
 68     // Add parenthesis
 69     function p(s:String):String {
 70         return ('('+s+')');
 71     }
 72     // Add quotes
 73     function q(s:String):String {
 74         return ('"'+s+'"');
 75     }
 76     var list:Array = [q(eventName)]; //Add the event to the array
 77     if (args.length > 0) list.push(JSON.encode(args)); // Add arguments to the array as a JSON object
 78     ExternalInterface.call(['jQuery'+p(q('#'+param.uploadifyID)), p(list.join(','))].join('.trigger')); // Trigger the event
 79 }
 80 
 81 // Random string generator for queue IDs
 82 function generateID(len:Number):String {
 83     var chars:Array = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'];
 84     var ID:String = '';
 85     var index:Number;
 86     for (var n:int = 0; n < len; n++) {
 87         ID += chars[Math.floor(Math.random() * 25)];
 88     }
 89     return ID;
 90 }
 91 
 92 // Load the button image
 93 function setButtonImg():void {
 94     if (param.buttonImg) {
 95         var btnLoader:Loader    = new Loader();
 96         var btnImage:URLRequest = new URLRequest(param.buttonImg);
 97         browseBtn.addChild(btnLoader);
 98         btnLoader.load(btnImage);
 99     }
100     if (!param.hideButton && !param.buttonImg) {
101         browseBtn.empty.alpha = 1;
102     }
103 }
104 setButtonImg();
105 
106 // Hide or show the button
107 function hideButton(hideValue:Boolean):void {
108     if (hideValue) {
109         browseBtn.empty.alpha = 0;
110     } else {
111         browseBtn.empty.alpha = 1;
112     }
113 }
114 
115 // Set the text on the button
116 function setButtonText():void {
117     if (param.buttonText) {
118         browseBtn.empty.buttonText.text = unescape(param.buttonText);
119     }
120 }
121 setButtonText();
122 
123 // This is important for clicking the button correctly
124 browseBtn.buttonMode = true;
125 browseBtn.useHandCursor = true;
126 browseBtn.mouseChildren = false;
127 
128 // Set the size of the button
129 function setButtonSize():void {
130     if (param.hideButton) {
131         browseBtn.width  = param.width;
132         browseBtn.height = param.height;
133     }
134     // Set the size of the button on the page
135     ExternalInterface.call('jQuery("#' + param.uploadifyID + '").attr("width",' + param.width + ')');
136     ExternalInterface.call('jQuery("#' + param.uploadifyID + '").attr("height",' + param.height + ')');
137 }
138 setButtonSize();
139 
140 // Setup the rollover animation
141 if (param.rollover) {
142     browseBtn.addEventListener(MouseEvent.ROLL_OVER, function (event:MouseEvent):void {
143         event.currentTarget.y = -param.height;
144     });
145     browseBtn.addEventListener(MouseEvent.ROLL_OUT, function (event:MouseEvent):void {
146         event.currentTarget.y = 0;
147     });
148     browseBtn.addEventListener(MouseEvent.MOUSE_DOWN, function (event:MouseEvent):void {
149         event.currentTarget.y = -(param.height * 2);
150     });
151 }
152 
153 // create the scriptData variable if it doesn't exist
154 if (!param.scriptData) {
155     param.scriptData = '';
156 }
157 
158 // Limit the file types
159 function setAllowedTypes():void {
160     allowedTypes = [];
161     if (param.fileDesc && param.fileExt) {
162         var fileDescs:Array = param.fileDesc.split('|');
163         var fileExts:Array = param.fileExt.split('|');
164         for (var n = 0; n < fileDescs.length; n++) {
165             allowedTypes.push(new FileFilter(fileDescs[n], fileExts[n]));
166         }
167     }
168 }
169 setAllowedTypes();
170 
171 // Set or get the variables
172 function uploadify_updateSettings(settingName:String, settingValue) {
173     if(settingValue == null) {
174         if (settingName == 'queueSize') {
175             return fileQueue.length;
176         }
177         return param[settingName];
178     } else {
179         param[settingName] = settingValue;
180         if(settingName == 'buttonImg') setButtonImg();
181         if(settingName == 'buttonText') setButtonText();
182         if(settingName == 'fileDesc' || settingName == 'fileExt') setAllowedTypes();
183         if(settingName == 'width' || settingName == 'height') setButtonSize();
184         if(settingName == 'hideButton') hideButton(settingValue);
185         return true;
186     }
187 }
188 
189 // Browse for Files
190 browseBtn.addEventListener(MouseEvent.CLICK, function():void {
191     if (objSize(activeUploads) == 0) { // Don't browse if it's uploading
192         if (!allowedTypes) {
193             (!param.multi) ? fileRefSingle.browse() : fileRefMulti.browse();
194         } else {
195             (!param.multi) ? fileRefSingle.browse(allowedTypes) : fileRefMulti.browse(allowedTypes);
196         }
197     }
198 });
199 
200 // Get the size of an object
201 function objSize(obj:Object):Number {
202     var i:int = 0;
203     for (var item in obj) {
204         i++;
205     }
206     return i;
207 }
208 
209 // Get actual folder path
210 function getFolderPath():String {
211     var folder:String = param.folder;
212     if (param.folder.substr(0,1) != '/' && param.folder.substr(0,4) != 'http') {
213         folder = param.pagepath + param.folder;
214         var folderParts:Array = folder.split('/');
215         for (var i = 0; i < folderParts.length; i++) {
216             if (folderParts[i] == '..') {
217                 folderParts.splice(i - 1, 2);
218             }
219         }
220         folder = folderParts.join('/');
221     }
222     return folder;
223 }
224 
225 // Get the array index of the item in the fileQueue
226 function getIndex(ID:String):Number {
227     var index:int;
228     for (var n:Number = 0; n < fileQueue.length; n++) {
229         if (fileQueue[n].ID == ID) {
230             index = n;
231         }
232     }
233     return index;
234 }
235 
236 // Check if a file with the same name is already in the queue
237 function inQueue(fileName:String):Object {
238     var obj:Object = new Object();
239     obj.testResult = false;
240     if (fileQueue.length > 0) {
241         for (var n = 0; n < fileQueue.length; n++) {
242             if (fileQueue[n].file.name == fileName) {
243                 obj.size       = fileQueue[n].file.size;
244                 obj.ID         = fileQueue[n].ID;
245                 obj.arrIndex   = n;
246                 obj.testResult = true;
247             }
248         }
249     }
250     return obj;
251 }
252 
253 // When selecting a file
254 function fileSelectSingleHandler(event:Event):void {
255     // Check if the filename already exists in the queue
256     fileItem = new Object();
257     fileItem.file = FileReference(event.target);
258     uploadify_clearFileUploadQueue(true);
259     var ID:String = generateID(6);
260     fileItem.ID = ID;
261     fileQueue.push(fileItem);
262     filesSelected = 1;
263     allBytesTotal = fileItem.file.size;
264     $trigger('uploadifySelect',ID,fileItem.file);
265     $trigger('uploadifySelectOnce',{
266         'fileCount'     : fileQueue.length,
267         'filesSelected' : filesSelected,
268         'filesReplaced' : filesReplaced,
269         'allBytesTotal' : allBytesTotal
270     });
271     filesSelected = 0;
272     filesReplaced = 0;
273     if (param.auto) {
274         if (param.checkScript) { 
275             uploadify_uploadFiles(null, false);
276         } else {
277             uploadify_uploadFiles(null, true);
278         }
279     }
280 }
281 
282 function fileSelectMultiHandler(event:Event):void {
283     var ID:String = '';
284     for (var n:Number = 0; n < fileRefMulti.fileList.length; n++) {
285         fileItem = new Object();
286         fileItem.file = fileRefMulti.fileList[n];
287         // Check if the filename already exists in the queue
288         var queueTest:Object = inQueue(fileRefMulti.fileList[n].name);
289         if (queueTest.testResult) {
290             allBytesTotal -= queueTest.size;
291             allBytesTotal += fileItem.file.size;
292             fileItem.ID    = fileQueue[queueTest.arrIndex].ID;
293             fileQueue[queueTest.arrIndex] = fileItem;
294             filesReplaced++;
295         } else {
296             if (fileQueue.length < param.queueSizeLimit) {
297                 ID = generateID(6);
298                 fileItem.ID = ID;
299                 fileQueue.push(fileItem);
300                 filesSelected++;
301                 allBytesTotal += fileItem.file.size;
302                 $trigger('uploadifySelect',ID,fileItem.file);
303             } else {
304                 $trigger('uploadifyQueueFull',param.queueSizeLimit);
305                 break;
306             }
307         }
308     }
309     $trigger('uploadifySelectOnce',{
310         'fileCount'     : fileQueue.length,
311         'filesSelected' : filesSelected,
312         'filesReplaced' : filesReplaced,
313         'allBytesTotal' : allBytesTotal
314     });
315     filesSelected = 0;
316     filesReplaced = 0;
317     if (param.auto) {
318         if (param.checkScript) { 
319             uploadify_uploadFiles(null, false);
320         } else {
321             uploadify_uploadFiles(null, true);
322         }
323     }
324 }
325 fileRefSingle.addEventListener(Event.SELECT, fileSelectSingleHandler);
326 fileRefMulti.addEventListener(Event.SELECT, fileSelectMultiHandler);
327 
328 // This function should run during upload so flash doesn't timeout
329 function uploadCounter(event:Event):void {
330     counter++;
331 }
332 
333 // Start the upload
334 function uploadify_uploadFiles(ID:String, checkComplete:Boolean):void {
335     if (!queueReversed) {
336         fileQueue.reverse();
337         queueReversed = true;
338     }
339     if (param.script.substr(0,1) != '/' && param.script.substr(0,4) != 'http') param.script = param.pagepath + param.script;
340     scriptURL = new URLRequest(param.script);
341     variables = new URLVariables();
342     (param.method.toUpperCase() == "GET") ? scriptURL.method = URLRequestMethod.GET : scriptURL.method = URLRequestMethod.POST;
343     if (param.scriptData != '') variables.decode(unescape(param.scriptData));
344     if (param.fileExt) variables.fileext = unescape(param.fileExt);
345     variables.folder = unescape(getFolderPath());
346     scriptURL.data = variables;
347     if (param.checkScript && !checkComplete) {
348         var fileQueueObj:Object = new Object();
349         if (ID) {
350             var index:int = getIndex(ID);
351             if (fileQueue[index].file) {
352                 fileQueueObj[fileQueue[index].ID] = fileQueue[index].file.name;
353             }
354             $trigger('uploadifyCheckExist',param.checkScript,fileQueueObj,param.folder,true);
355         } else {
356             for (var n:Number = fileQueue.length - 1; n > -1; n--) {
357                 if (fileQueue[n]) {
358                     fileQueueObj[fileQueue[n].ID] = fileQueue[n].file.name;
359                 }
360             }
361             $trigger('uploadifyCheckExist',param.checkScript,fileQueueObj,param.folder,false);
362         }
363     } else {
364         if (ID && fileQueue[getIndex(ID)].file) {
365             uploadFile(fileQueue[getIndex(ID)].file, getIndex(ID), ID, true);
366         } else {
367             for (n = fileQueue.length - 1; n > -1; n--) {
368                 if (objSize(activeUploads) < parseInt(param.simUploadLimit)) {
369                     if (!activeUploads[fileQueue[n].ID] && fileQueue[n].file) {
370                         uploadFile(fileQueue[n].file, n, fileQueue[n].ID, false);
371                     }
372                 } else {
373                     break;
374                 }
375             }
376         }
377     }
378 }
379 
380 function queueIsNotEmpty(item:*, index:int, array:Array):Boolean {
381     return (item.file != '');
382 }
383 
384 // Upload each file
385 function uploadFile(file:FileReference, index:int, ID:String, single:Boolean):void {
386     var startTimer:Number      = 0;
387     var lastBytesLoaded:Number = 0;
388     var kbsAvg:Number          = 0;
389     
390     function fileOpenHandler(event:Event) {
391         startTimer = getTimer();
392         $trigger('uploadifyOpen',ID,event.currentTarget);
393     }
394     
395     function fileProgressHandler(event:ProgressEvent):void {
396         var percentage:Number = Math.round((event.bytesLoaded / event.bytesTotal) * 100);
397         if ((getTimer()-startTimer) >= 150) {
398             kbs = ((event.bytesLoaded - lastBytesLoaded)/1024)/((getTimer()-startTimer)/1000);
399             kbs = int(kbs*10)/10; 
400             startTimer = getTimer();
401             if (kbsAvg > 0) {
402                 kbsAvg = (kbsAvg + kbs)/2;
403             } else {
404                 kbsAvg = kbs;
405             }
406             allKbsAvg = (allKbsAvg + kbsAvg)/2;
407         }
408         allBytesLoaded += (event.bytesLoaded - lastBytesLoaded);
409         lastBytesLoaded = event.bytesLoaded;
410         $trigger('uploadifyProgress',ID,event.currentTarget,{
411             'percentage'     : percentage,
412             'bytesLoaded'    : event.bytesLoaded,
413             'allBytesLoaded' : allBytesLoaded,
414             'speed'          : kbs
415         });
416     }
417     
418     function fileCompleteHandler(event:DataEvent):void {
419         if (kbsAvg == 0) {
420             kbs = (file.size/1024)/((getTimer()-startTimer)/1000);
421             kbsAvg = kbs;
422             allKbsAvg = (allKbsAvg + kbsAvg)/2;
423         }
424         
425         allBytesLoaded -= lastBytesLoaded;
426         allBytesLoaded += event.currentTarget.size;
427         
428         $trigger('uploadifyProgress',ID,event.currentTarget,{
429             'percentage'     : 100,
430             'bytesLoaded'    : event.currentTarget.size,
431             'allBytesLoaded' : allBytesLoaded,
432             'speed'          : kbs
433         });
434         $trigger('uploadifyComplete',ID,{
435                 'name'             : event.currentTarget.name,
436                 'filePath'         : getFolderPath() + '/' + event.currentTarget.name,
437                 'size'             : event.currentTarget.size,
438                 'creationDate'     : event.currentTarget.creationDate,
439                 'modificationDate' : event.currentTarget.modificationDate,
440                 'type'             : event.currentTarget.type
441             },
442             escape(event.data),{
443             'fileCount' : (fileQueue.length-1),
444             'speed'     : kbsAvg
445         });
446         filesUploaded++;
447         fileQueue.splice(getIndex(ID),1);
448         delete activeUploads[ID];
449         if (!single) {
450             uploadify_uploadFiles(null, true);
451         }
452         event.currentTarget.removeEventListener(DataEvent.UPLOAD_COMPLETE_DATA, fileCompleteHandler);
453         if (!fileQueue.some(queueIsNotEmpty) && objSize(activeUploads) == 0) {
454             $trigger('uploadifyAllComplete',{
455                 'filesUploaded'  : filesUploaded,
456                 'errors'         : errors,
457                 'allBytesLoaded' : allBytesLoaded,
458                 'speed'          : allKbsAvg
459             });
460             resetVars();
461         }
462     }
463     
464     // Add all the event listeners
465     file.addEventListener(Event.OPEN, fileOpenHandler);
466     file.addEventListener(ProgressEvent.PROGRESS, fileProgressHandler);
467     file.addEventListener(DataEvent.UPLOAD_COMPLETE_DATA, fileCompleteHandler);
468     
469     // Reset all the numbers
470     function resetVars() {
471         filesUploaded  = 0;
472         errors         = 0;
473         allBytesLoaded = 0;
474         allBytesTotal  = 0;
475         allKbsAvg      = 0;
476         filesChecked   = 0;
477         queueReversed  = false;
478     }
479     
480     // Handle all the errors
481     file.addEventListener(HTTPStatusEvent.HTTP_STATUS, function(event:HTTPStatusEvent):void {
482         if (errorArray.indexOf(ID) == -1) {  
483             $trigger('uploadifyError',ID,event.currentTarget,{
484                 'type' : 'HTTP',
485                 'info' : event.status
486             });
487             finishErrorHandler(ID);
488         }
489     });
490     file.addEventListener(IOErrorEvent.IO_ERROR, function(event:IOErrorEvent):void {
491         if (errorArray.indexOf(ID) == -1) {
492             $trigger('uploadifyError',ID,event.currentTarget,{
493                 'type' : 'IO',
494                 'info' : event.text
495             });
496             finishErrorHandler(ID);
497         }
498     });
499     file.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function(event:SecurityErrorEvent):void {
500         if (errorArray.indexOf(ID) == -1) { 
501             $trigger('uploadifyError',ID,event.currentTarget,{
502                 'type' : 'Security',
503                 'info' : event.text
504             });
505             finishErrorHandler(ID);
506         }
507     });
508     
509     // Common routines used by all errors
510     function finishErrorHandler(ID:String) {
511         errorArray.push(ID);
512         fileQueue[getIndex(ID)].file = '';
513         delete activeUploads[ID];
514         if (!single) {
515             uploadify_uploadFiles(null, true);
516         }
517         errors++;
518         if (!fileQueue.some(queueIsNotEmpty)) {
519             if (root.hasEventListener(Event.ENTER_FRAME)) {
520                 root.removeEventListener(Event.ENTER_FRAME, uploadCounter);
521             }
522             $trigger('uploadifyAllComplete',{
523                 'filesUploaded'  : filesUploaded,
524                 'errors'         : errors,
525                 'allBytesLoaded' : allBytesLoaded,
526                 'speed'          : allKbsAvg
527             });    
528             resetVars();
529         }
530     }
531     
532     if (param.sizeLimit && file.size > parseInt(param.sizeLimit)) {
533         if (errorArray.indexOf(ID) == -1) { 
534             $trigger('uploadifyError',ID,file,{
535                 'type' : 'File Size',
536                 'info' : param.sizeLimit
537             });
538             finishErrorHandler(ID);
539         }
540     } else {
541         file.upload(scriptURL, param.fileDataName);
542         activeUploads[ID] = true;
543     }
544 }
545 
546 function uploadify_cancelFileUpload(ID:String, single:Boolean, clearFast:Boolean):void {
547     var index:int = getIndex(ID);
548     var fileObj:Object = new Object();
549     if (fileQueue[index].file) {
550         fileObj = fileQueue[index].file;
551         fileQueue[index].file.cancel();
552         allBytesTotal -= fileQueue[index].file.size;
553     }
554 
555     fileQueue.splice(index,1);
556 
557     if (activeUploads[ID]) {
558         delete activeUploads[ID];
559         uploadify_uploadFiles(null, true);
560         if (root.hasEventListener(Event.ENTER_FRAME) && objSize(activeUploads) == 0) {
561             root.removeEventListener(Event.ENTER_FRAME, uploadCounter);
562         }
563     }
564 
565     $trigger('uploadifyCancel',ID,fileObj,{
566         'fileCount'     : (fileQueue.length),
567         'allBytesTotal' : allBytesTotal
568     },clearFast);
569 }
570 
571 // Cancel all uploads
572 function uploadify_clearFileUploadQueue(clearFast:Boolean):void {
573     if (!queueReversed) {
574         fileQueue.reverse();
575         queueReversed = true;
576     }
577     for (var n:Number = fileQueue.length - 1; n >= 0; n--) {
578         uploadify_cancelFileUpload(fileQueue[n].ID, false, clearFast);
579     }
580     if (root.hasEventListener(Event.ENTER_FRAME)) {
581         root.removeEventListener(Event.ENTER_FRAME, uploadCounter);
582     }
583     $trigger('uploadifyClearQueue');
584     filesUploaded  = 0;
585     errors         = 0;
586     allBytesLoaded = 0;
587     allBytesTotal  = 0;
588     allKbsAvg      = 0;
589     filesChecked   = 0;
590     queueReversed  = false;
591 }
592 
593 // Create all the callbacks for the functions
594 ExternalInterface.addCallback('updateSettings', uploadify_updateSettings);
595 ExternalInterface.addCallback('startFileUpload', uploadify_uploadFiles);
596 ExternalInterface.addCallback('cancelFileUpload', uploadify_cancelFileUpload);
597 ExternalInterface.addCallback('clearFileUploadQueue', uploadify_clearFileUploadQueue);
View Code

  现在我们能够得出这样一个结论:Uploadify本质上是一个基于Flash的jQuery上传插件.那么它到底能不能跨域呢?这里我们要考虑两个安全模型.一个是浏览器的同源策略,jQuery与目标服务器的交互是过Jsonp方式来实现回避浏览器的同源策略。还有一个就是Flash的安全沙箱,分析之前我们还是先完成金色海洋想要的实验.

 

环境准备

       我们在IIS中部署两个站点,既然是验证跨域我们为这两个站点绑定主机头;

   站点A:www.a.com 端口83 站点B:www.b.com 端口84

   

   修改Host文件(文件位置c:\Windows\System32\drivers\etc\hosts)

 

127.0.0.1 www.a.com

 

127.0.0.1 www.b.com

   注意:为了避免偶然因素的影响,后面每一次修改之后我都会重启IIS,使用新的浏览器窗口进行测试。

 

  1. 首先验证两个站点是否正常运行:站点A使用'script': 'http://www.a.com:83/Uploader.ashx', 站点B使用'script': 'http://www.b.com:84/Uploader.ashx',即当前域页面上传到当前域.测试通过
  2. 将站点A上传的目标服务域修改成站点B,即修改'script': 'http://www.b.com:84/Uploader.ashx',测试结果见下面的截图

Uploadify跨域上传原理 

    3.看到上面的Security Error了么,我们通过在站点B下面添加一个crossdomain.xml,该文件的内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE cross-domain-policy SYSTEM  
    "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd" >  
<cross-domain-policy>  
    <site-control permitted-cross-domain-policies="all" />  
    <allow-access-from domain="*" />  
    <allow-http-request-headers-from domain="*" headers="*"/>
</cross-domain-policy>

添加了这个文件之后,站点A上传文件到站点B成功!

Uploadify跨域原理

      上面通过添加了crossdomain.xml之后就实现了跨域上传文件,关键点在于Flash的安全沙箱策略;Flash安全策略简单讲:同一个域的属于同一个沙箱,同一个沙箱的可以互相访问.如果要访问另外一个沙箱的内容就要在另外一个沙箱定义信任,这种信任策略就定义在crossdomain.xml中。在我们的实验中就是在站点B的策略文件crossdomain.xml中定义了对站点A的信任,只不过我偷懒让站点B信任所有外部域名的访问。

     对于crossdomain.xml还有两点细节:1.这个文件的要放在站点的根目录下而且文件名固定 2.跨域访问端口在1024以下必须要通过策略文件来定义信任关系。换句话说端口大于等于1024隐式开放访问权限。策略文件的详细说明请点击这里查看。

总结   

    对于金色海洋的问题解答:之所以你可以上传到另外一个端口的站点时因为IP一样,如果是上传到另外一个域名下,需要再目标服务器的根目录下添加Flash安全策略文件.

     Uploadify本质上是一个基于Flash的jQuery上传插件.跨域上传的情况牵扯到两个安全模型,一个使浏览器的同源策略,一个使是Flash的安全沙箱策略;我们组合使用jQuery的Jsonp和策略文件实现了跨域上传.

     好吧,就到这里,周末愉快

 

     测试文件下载:Web1.rar crossdomain.xml