引用: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,页面代码如下:
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" />
通过单步调试,我发现页面上会通过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文件里面.
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);
现在我们能够得出这样一个结论:Uploadify本质上是一个基于Flash的jQuery上传插件.那么它到底能不能跨域呢?这里我们要考虑两个安全模型.一个是浏览器的同源策略,jQuery与目标服务器的交互是过Jsonp方式来实现回避浏览器的同源策略。还有一个就是Flash的安全沙箱,分析之前我们还是先完成金色海洋想要的实验.
环境准备
我们在IIS中部署两个站点,既然是验证跨域我们为这两个站点绑定主机头;
站点A:www.a.com 端口83 站点B:www.b.com 端口84
修改Host文件(文件位置c:WindowsSystem32driversetchosts)
127.0.0.1 www.a.com
127.0.0.1 www.b.com
注意:为了避免偶然因素的影响,后面每一次修改之后我都会重启IIS,使用新的浏览器窗口进行测试。
- 首先验证两个站点是否正常运行:站点A使用'script': 'http://www.a.com:83/Uploader.ashx', 站点B使用'script': 'http://www.b.com:84/Uploader.ashx',即当前域页面上传到当前域.测试通过
- 将站点A上传的目标服务域修改成站点B,即修改'script': 'http://www.b.com:84/Uploader.ashx',测试结果见下面的截图
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