FLASH論壇-Flash之神魂顛倒Adobe Flash 大家園Flash教學/下載分享區Flash教學分享區 → Flash SWFUpload debug之旅

MSDN 研討會資料下載 熱門租屋行情 網路行銷秘訣大公開 磷蝦油=比魚油更強 Microsoft Silverlight
減肥診所 雷射抽脂 ASP.NET完全攻略與快速上手 買墨水送神幣喔 貸款
近視雷射手術 電波拉皮 隆乳 胎毛筆 當鋪
投影機 生日禮物 借錢 調整型內衣 白蟻
氣球 眼袋 虛擬主機神魂特惠 大陸新娘 NCCU集中營
網頁設計 愛情城市 保險套 液晶電視

  共有1811人閱讀過本文章折疊列印

主題:Flash SWFUpload debug之旅

帥哥,在線上噢!
TWG
  1樓 個人化首頁 | QQ | 個人資料 | 搜尋 | EMAIL | 首頁 | |

加到: FunP 書籤加到: 黑米書籤加到: MyShare 書籤加到: 美味書籤加到: Furl  書籤加到: YaHoo 分享書籤加到: Google 書籤加到: UDN 書籤加到: Technorati 書籤



加好友 悄悄話 •神•魂•顛•倒•
等級:站長 文章:2578 經驗:25213 威望:7 精華:7 註冊:2002-4-12 15:57:00
Flash SWFUpload debug之旅  發表心情 Post By:2008-5-19 0:53:00



Flash SWFUpload debug之旅

由於項目需要,這兩天研究了一下SWFUpload,一個JS和AS結合實現的上傳「功能包」,簡單來說原理就是利用flash的FileReference類在客戶端完成檔案訊息的過濾,包括類型、大小,以及上傳的實時進度訊息,然後利用externalInterface來跟客戶端的JS通信,提供了一系列屬性、方法和事件觸發時的回調函數介面,將UI的主動權完全由FLASH轉交給了JS來操作。(PS:目前版本還沒有Release,有些功能和文檔還不全,疑惑之處可直接翻程式碼看看。等我項目結束時希望SWFUpload放出正式版和官方確定的介面文檔了,到時再針對傳統檔案上傳的各種方式和SWFUpload功能包嘮叨幾句。)這裡只嘮叨一下目前這個版本存在的一個BUG,在IE6內核的多語法瀏覽器中(例如GreenBrowser和Maxthon),一旦頁面更新過後,SWFUpload的功能就不正常了,或許你也正為這個發愁了,可以先跳到這裡檢視臨時解決方案。

  最初用SWFUpload做demo的時候就發現了目前存在一個小BUG:我機器上的瀏覽器環境是GreenBrowser(IE6內核),打開上傳頁面,一切功能都正常,此時如果更新或者強刷一下頁面,那麼當點選檔案瀏覽按鈕時(觸發selectFile事件),debug訊息中就會提示「Could not find Flash element」,而在FF、IE7下面功能很正常,更奇怪的是在我隨後的debug過程中我發現在單獨的IE6,也就是系統提供的那個單視窗的IE6下居然功能也很正常。後來又在ie6內核下測試了maxthon同樣存在和GreenBrowser一樣的問題,難道這個跟多語法瀏覽器也有關係?第一反應是我的demo有問題。於是去SWFUpload的官方demo驗證了下,發現居然也存在這個問題。哎∼又是IE惹的禍。開始Debug之旅吧。


  一、首先用DOM檢視器在IE下看看更新前後DOM元素中flash元素的變化
  有了FireBug以後,讓IE下的幾個調試工具顯得太簡陋了,不過至少我還可以直接檢視到DOM元素的變化。奇怪的是更新前後,FLASH元素都完整地出現在DOM中,那為何會提示「Could not find Flash element」呢?
  
  二、 跟蹤檔案瀏覽的點選事件selectFile
  到SWFUpload庫中能看到擴充了一個selectFile的方法。

SWFUpload.prototype.selectFile = function () {
    var movie_element = this.getMovieElement();
    if (movie_element !== null && typeof(movie_element.SelectFile) === "function") {
        try {
            movie_element.SelectFile();
        }
        catch (ex) {
            this.debug("Could not call SelectFile: " + ex);
        }
    } else {
        this.debug("Could not find Flash element");
    }
 
};
  
難道「Could not find Flash element」是這裡輸出來的?於是將這個語法給註釋掉再測試,果然沒有看到「沒有找到flash元素」的debug訊息了,這就確定了是這個方法裡出現了異常,仔細看了下這個if判斷,這裡不但對flash元素的存在性做了判斷,還需要判斷flash元素裡是否存在SelectFile方法。個人覺得這個雙保險檢測是沒有錯,但這個debug訊息未免就太籠統了,typeof(movie_element.SelectFile) === "function" 如果是false那也不能說明是flash元素不存在。之前我總結flash和js通信的時候也做過externalInterface的demo,猜測一下這裡的實現原理:SelectFile方法是externalInterface類用addCallback方法在 flash中將一個名為SelectFile的方法註冊到容器中可供JS叫用,也就是現在看到的這種形式movie_element.SelectFile()。那麼理論上如果flash元素存在,那麼typeof(movie_element.SelectFile) === "function"肯定是true,可是在IE6這個總是很另類但又是使用者佔有率最高的瀏覽器裡這個理論還真就出問題了,下面可以來證明一下。


  三、觀察瀏覽器更新前後movie_element.SelectFile的變化
  重新打開一個新頁面測試SWFUpload的官方demo,測試檔案瀏覽功能,正常。於是在地址欄輸入

javascript:alert(document.getElementById("SWFUpload_0").SelectFile);void 0;
(PS:IE下的調試方法實在是太簡陋了,還好廣大開發者的智慧是無窮的。)
這裡的SWFUpload_0是SWFUpload產生的flash元素時指定的id,這個可以從dom節點中看到。如果你想更詳細地確認其中細節可以看SWFUpload中的initSWFUpload方法,事實上這裡會有一個實例隊列,因為這個demo中就是一個實例,所以這個flash的id在隊列中是SWFUpload_0。ENTER執行上面的JS,正常情況下你會看到如下訊息:

function () { return eval(instance.CallFunction("<invoke name=\""+name+"-:special:1:- returntype=-:special:1:-javascript-:special:1:->" + __flash__argumentsToXML(arguments,0) + "</invoke>")); }

  從這段程式碼的特徵上也驗證我之前的猜測,SelectFile確實是flash中註冊的一個方法。

然後更新此頁面,再到地址欄輸入ENTER,HOHO你會得到undefined訊息。SWFUpload的開發者可能也沒有想到理論上很保險的做法到IE6下會有如此怪異的行為。Flash還存在,為什麼其中addCallback註冊的方法會「消失」呢?難道是externalInterface類本身有問題?於是又把自己以前的一個demo拿出來測試,發現更新前後功能依然很正常,匪夷所思。仔細對比了下兩個demo的差異,最後將疑點鎖定在了flash的寫入方式,SWFUpload是用JS將flash寫入到dom中的,我自己的demo中flash元素是直接寫死在HTML程式碼裡的。


  四、測試不同的flash寫入方式對addCallback方法的影響

  AW說SWFUpload中寫入flash的方法是從SWFObject庫裡抽出來的,這個SWFObject的官方說明也證實了這點。打開SWFUpload庫確實也可以看到SWFObject的幾個核心方法。那這就確定了SWFUpload寫入flash和SWFObject原理上一樣的,於是採用SWFobject對我之前的demo做測試。
  1、首先用最原始的方法將flash靜態寫入到頁面中(檢視實例)
  關於靜態插入flash到頁面的不同方式的細節以及他們對JS造成的影響隨後再寫個詳細的東東,這裡先不做實際探討,暫且使用最常用的object和embed混合的方式。
  第一個按鈕是js叫用as中註冊的say方法;
  第二個按鈕是測試flash元素是否在頁面中,如果在頁面中,那麼列印出他的innerHTML屬性;
  第三個按鈕是測試flash元素中addCallBack註冊的方法是否還存在,如果存在那麼將其列印出來;
  當頁面第一次打開的時候分別點選三個按鈕,callback註冊的say方法呼叫正常,flash元素存在,其中註冊的say方法也存在。
  下面將頁面更新一次再測試,測試結論和剛才一樣,都很正常。這就確保了我目前採用的flash中addCallBack註冊的say方法功能是正常的。

  2、利用SWFObject寫入flash到頁面中(檢視實例)
  同樣測試更新前後的變化,更新前功能正常,而將頁面更新一次再測試,就跟我之前描述的bug一樣,怪異現象出現了,callback註冊的say方法呼叫失敗了,flash元素是存在的,但其中註冊的say方法也不見了。

到這步為止已經找到辦法解決項目中遇到的SWFUpload的更新BUG了,將SWFUpload中寫入flash的方法變成靜態寫入即可,事實上當時考慮項目進度我確實是這麼做的,嘿嘿,儘管方法太笨。由於SWFUpload實例有很多設定需要設定到FlashVars中,所以做這個靜態寫入時最好先將SWFupload的getFlashHTML方法返回值列印出來(PS:需要針對IE和之外的瀏覽器,實際上就是Object和embed兩種不同flash的插入方式做一個判斷輸出,切忌不能將他們同時寫入頁面,否則會造成SWFUpload無法正確找到實例的flash元素。)


  上面的解決方案只是曲線救國,考慮到以後項目中的靈活使用和維護,還是需要把其中的問題找出來,再回到我們的Debug進程來。現在已經證明問題出在flash的寫入方式上。刨開SWFObject的程式碼能夠很清晰看出它的原理:根據實例化時的最基本參數設定和屬性、flashvars設定方法來針對object和embed兩種不同的插入方法構造出兩個不同的flashDom節點。當write方法叫用時,將此節點寫入到目標容器的innerHTML中。難道是innerHTML在作怪?


  五、跟蹤innerHTML寫入flash節點

  還是針對上面的demo做測試,將SWFObject叫用write前構造出來的flash節點字串和write之後瀏覽器裡flash節點的html程式碼分別列印出來比較。

IE6:
Write叫用前:
<object id="demo" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="300" height="100" style="undefined"><param name="movie" value="demo.swf" /><param name="bgcolor" value="#fff" /><param name="quality" value="high" /></object>
 
Write叫用後:
<OBJECT id=demo height=100 width=300 classid=clsid:D27CDB6E-AE6D-11cf-96B8-444553540000><PARAM NAME="_cx" VALUE="7938"><PARAM NAME="_cy" VALUE="2646"><PARAM NAME="FlashVars" VALUE=""><PARAM NAME="Movie" VALUE="demo.swf"><PARAM NAME="Src" VALUE="demo.swf"><PARAM NAME="WMode" VALUE="Window"><PARAM NAME="Play" VALUE="0"><PARAM NAME="Loop" VALUE="-1"><PARAM NAME="Quality" VALUE="High"><PARAM NAME="SAlign" VALUE=""><PARAM NAME="Menu" VALUE="-1"><PARAM NAME="Base" VALUE=""><PARAM NAME="AllowScriptAccess" VALUE=""><PARAM NAME="Scale" VALUE="ShowAll"><PARAM NAME="DeviceFont" VALUE="0"><PARAM NAME="EmbedMovie" VALUE="0"><PARAM NAME="BGColor" VALUE=""><PARAM NAME="SWRemote" VALUE=""><PARAM NAME="MovieData" VALUE=""><PARAM NAME="SeamlessTabbing" VALUE="1"><PARAM NAME="Profile" VALUE="0"><PARAM NAME="ProfileAddress" VALUE=""><PARAM NAME="ProfilePort" VALUE="0"><PARAM NAME="AllowNetworking" VALUE="all"><PARAM NAME="AllowFullScreen" VALUE="false"></OBJECT>

  顯然,IE瀏覽器對object便簽做了很多處理,innerHTML強大功能的背後到底隱藏了什麼?起初我懷疑是innerHTML有快取機制,因此更新的時候造成了bug,為了驗證對著這個猜測,我給寫入的flash字串中新增了一個時間戳來「更新快取」,BUG依然存在,排除了這個快取猜測。
如果換作其他的動態寫入是否也有這個BUG呢?於是想到了document.write,在這個實例中,針對object和embed我做了單獨輸出,測試發現document.write寫入前後的flash節點改變跟innerHTML前後的改變是一致的,但更新BUG沒有出現。難道這個BUG真的是innerHTML的內部實現造成的,如果真是這樣那麻煩就大了,總不能指望微軟現在來改IE6吧。


  六、只有google一下innerHTML的內部機制了

  首先google了一下innerHTML和externalInterface,想看看是否也有人遇到這個問題了。直接G到一篇SWFObject論壇上的一個討論,對於Geoff在這裡提到了幾種造成externalInterface通信失敗的可能性,我的實例中都沒有出現,可是BUG依然存在。不過這則討論中的一個細節引起了我注意,Geoff說他們在開發中一直都在使用externalInterface和js通信,從來沒有出現過bug,我也仔細看了他給出的demo,這裡是AS調Js的方法。難道innerHTM只對addCallBack有負作用,call是正常的?於是我又做了一個demo,這真是一個讓我很無語的結論,原來call方法真的是有效的。也就是說innerHTML只對addCallBack有更新bug。隨後google到一個 flex寫的externalInterface通信的範例更讓我覺得無語。我把這個flex寫的swf檔案儲存到了本地,並叫用了其中addCallBack註冊的方法,檢視實例,「靈異事件」發生了,無論我怎麼更新頁面,此前的bug都沒有出現。一下讓我將懷疑對像從innerHTML轉移到了AS2中的externalInterface類本身。不過這裡還有一個疑問就是為何只在IE6內核的多語法瀏覽器下才有此BUG呢?個人覺得可能性較大的原因是AS2中的externalInterface類確實還不完善,對於多語法瀏覽器中更複雜的window對象的兼容做得還不夠好。而flex中採用的externalInterface機制可能更完善了。


  下班回來從7:30到11:30,整整四個小時的Debug最終結論居然出是因為AS2的內部實現有bug,在SWFUpload官方更改自己AS內部實現前,看來暫時只有先採用曲線救國的方式了:


  一、之前所提到的實例產生時,直接列印出SWFUpload中getFlashHTML方法針對object和embed的不同輸出,然後將這兩段程式碼分別針對不同瀏覽器做硬編碼輸出,切忌這兩段程式碼不能共存。


  二、禁止掉SWFUpload中loadFlash,然後擴充一個方法,利用document.write在指定dom位置叫用,動態寫入flash元素。目前項目中我已經這樣做了,比第一種方法的擴充性要高點。



網路的事情,讓網路解決

[ 逛網路就像是在探險 ]

    神 魂 顛 倒 T W G

http://bbs.flash2u.com.tw

http://tw.myblog.yahoo.com/flash2u-twg

http://flash-silverlight.blogspot.com/

http://flash2u.spaces.live.com/?_c02_owner=1
近視雷射手術 支持(0中立(0反對(0回到頂部