公司之前有這樣一個業務需求:
一名同僚做出檔案a0和b0,然後将a0加密為a1、b0加密為b1;再将檔案a0、a1、b0和b1上傳至伺服器M;同時要将伺服器N上的資料表添加一條記錄,該記錄的ID就是前面四個檔案的檔案名(擴充名各不相同)。另外說一句,公司網站也在伺服器N上,一會兒會用到這點。
公司原來的實作模式是這樣的:
程式員小x用VB寫了一個軟體,能将文本檔案生成檔案a0,并将檔案a0加密為a1,還生成一個access資料庫檔案,通過軟體界面收集到的資訊,生成一條記錄為稍後向伺服器N添加記錄提供字段值;
程式員小y用C++的MFC寫了一個軟體,能将做好的b0檔案加密為b1(加密過程調用公司同僚小z的一個dll庫),再根據access資料庫檔案的資訊将a0、a1、b0、b1上傳至伺服器M,最後将伺服器N上的資料表添加記錄。
公司現在想要用B/S模式實作檔案上傳的功能,而且目前程式員小x和小y都已經離職,隻剩C++程式員小z。我是C#程式員,不太會C++和VB,對現有的狀況和公司的要求仔細考慮了一下,做出如下方案:
因為程式員小x的VB軟體涉及到a0檔案的生成和加密,以及該記錄各字段資訊的收集功能,公司裡也沒有VB程式員,改動程式比較困難,重新用C++或C#寫一個軟體工作量又大(老闆要求盡快完成,且不要弄得太複雜、太麻煩),是以小x的VB軟體不動;再看程式員小y的MFC程式軟體,它實作了b0檔案的加密(調用小z的庫),實作4個檔案的上傳和向伺服器N中的資料庫寫記錄(根據access檔案)的功能,是以小y的軟體不要了;
而讓小z做一個簡單的MFC程式軟體,實作如下幾點功能:1、讓使用者選擇一個access檔案(使用者用小x軟體生成的access檔案),程式讀取資料表資訊;2、根據資料表中各記錄的ID值把指定檔案夾中b0檔案加密成為b1檔案;3、将資料表及表中各記錄ID值對應的a0、a1、b0、b1檔案添加到一個壓縮封包件。最後讓使用者通路我寫好的一個頁面,有一個上傳flash插件和一個儲存按鈕,具體功能實作如下。
上傳flash插件選擇壓縮封包件,上傳flash插件指向背景ashx檔案,将這個壓縮封包件儲存至伺服器N站點(公司網站)下的一個檔案夾中;這個上傳檔案的功能做了前背景的驗證,前台用js做檔案類型、長度等資訊驗證,而背景也對HttpPostFiles對象裡的相關屬性驗證;伺服器儲存完畢後,使用者在頁面點選儲存按鈕,按鈕的點選事件執行如下操作:1、進行操作合法性驗證;2、解壓縮檔案包;3、找到access資料庫檔案,并讀取其中資料資訊;4、循環周遊每一行,執行FTP上傳至伺服器M,每上傳成功一條記錄對應的所有檔案,便将這條記錄添加到伺服器N中的資料庫中。
操作合法性驗證:目前登入使用者是否為預期的使用者,指定的壓縮封包件是否存在。
解壓縮檔案包:用了一個解壓縮庫,将壓縮包内的檔案解壓縮到指定的檔案夾中。
讀取access資料庫檔案:若不存在則傳回,否則将資料表中資料記錄讀取到DataSet中。
循環周遊資料表,并執行FTP上傳:循環DataTable,每次擷取DataRow後,将記錄ID對應的4個檔案上傳至伺服器M上,確定所有檔案都上傳成功後,向資料庫(公司網站伺服器就是伺服器N)寫入記錄,在上傳之前判斷該記錄ID是否已經存在在伺服器資料庫中,若存在則跳過,FTP上傳也用了一個庫。額外做了一個資料表,記錄上傳狀态,同時在FTP上傳功能代碼段中加入了寫日志檔案功能,以便确定上傳失敗的原因。
最後對實作這個功能過程中遇到的一些問題及解決思路:
一、用小檔案測試上傳時,上傳功能正常,可是換了真正的大檔案時,上傳功能出錯。
一開始沒意識到是asp.net配置的問題,因為是上傳flash插件無法将請求送出到ashx檔案,而且用VS調試運作也不行,用IIS運作則報500或404錯誤。還以為是項目或flash插件出了問題,可是調試都不進斷點,是以找不到原因,後來突然換回之前的小檔案,功能正常。于是乎知道了答案:asp.net有個預設的請求長度限制,這個要在web.config中進行設定。<httpRuntime maxRequestLength="820000" executionTimeout="14400"/>maxRequestLength屬性就是,其預設值為4096(4MB),注意除非做上傳功能,否則這個值不要設定過大,因為這個值的意義在于防止拒絕服務攻擊;還有executionTimeout屬性,這個意思是在被 ASP.NET 自動關閉前,允許執行請求的最大秒數,預設值為110秒(.NET1.0和.NET1.1預設值為90秒);還有一個是Session逾時<sessionState timeout="240"/>預設值為20分鐘。
二、上傳一條記錄(也就是4個檔案)時沒有問題,可上傳10條記錄時過一段時間就無響應了,經過多次觀察發現這個時間是20分鐘左右(根據日志檔案最後一次寫入時間判斷)。
一開始以為是網絡連接配接斷了,可是也應該在我的日志檔案中抛出異常,可是沒有。經常是某檔案開始上傳,之後就沒下文了,就不再寫日志檔案了。經過多次試驗,覺得可以認為是程式運作中斷,但是是什麼原因呢?後來知道IIS中有一項設定,IIS-網站站點所用的應用程式池-右鍵進階設定-閑置逾時屬性,系統預設值為20分鐘,這下可以确定了,就是20分鐘沒人請求網站,IIS認為閑置逾時,是以将程序停止,導緻程式運作中斷。因為本地測試是在我電腦上的IIS,是以隻有我一個人通路,而我主要測試上傳,是以上傳後就一直在等待,也沒有通路其他頁面。而真正的網站可能會被很多人通路,是以這個情況不大容易出現。
三、上傳檔案後,進行儲存操作,伺服器端需要進行讀資料庫、解壓檔案、FTP上傳、寫資料庫,10個檔案時大約用1分鐘,是以我想将這些耗時操作放到異步線程中執行,如何實作?
一開始以為開個新線程就能實作,多線程就代表異步執行嗎?百度也沒有查到new Thread(某個方法),然後.Start(),是不是異步執行,網上隻說Invoke是同步執行,而BeginInvoke是異步執行。後來終于查到用委托的BeginInvoke就是異步執行,同時博問中有個回答者給出了委托的BeginInvoke是否新開線程的測試代碼,經過測試委托的方法是在新線程中執行的,這樣就堅定了我的信心。而我也沒有自己寫委托,網上介紹.NET3.5新增了系統委托Action和Func(可能還有其他),剛好我的站點用.NET4.0,是以就用它,同時還用到匿名委托,這樣參數和傳回值都不需要有,于是果斷用Action(不帶任何泛型,帶一個泛型則說明有一個參數,最多可能有16個,而Func至少有一個泛型,最後一個泛型為傳回值,必須有傳回值,其餘泛型為參數,可以沒有參數,最多可能有16個),并在Action對象.BeginInvoke()方法後響應輸出給用戶端提示資訊,而異步線程在伺服器端自行運作。
四、測試本地項目打開頁面時,javascript控制台顯示一直有腳本在不斷執行請求。(2014-3-11補充)
一開始以為複制粘貼已有代碼時,把不相關的JS腳本代碼一并貼過來了,檢查并沒有發現這樣的腳本。這是怎麼回事呢?難道電腦中病毒了,系統或浏覽器被劫持,被惡意注入JS自動執行腳本了嗎?換了幾個浏覽器都有同樣的現象發生,用本機架設的IIS伺服器通路IP位址頁面和用VS通路localhost頁面,仍然有這個現象發生。回頭再找開發者工具中的HTML功能,發現裡面确實生成了一段我沒寫過的JS腳本代碼,将其動态删除後,不斷執行請求的JS腳本停止運作了。而且發送請求的位址是以localhost位址發送的,并且端口号也跟VS調試頁面所用端口不一樣。後來在開發者工具的網絡界面中發現這個請求中含有一個關鍵詞“Browser Link”,于是乎用這個關鍵詞百度了一下,居然有結果,而且就是我要尋找的原因。原因是VS2013(我之前一直使用VS2010,最近在更新使用VS2013版本的)為調試Web項目新增一個功能,多浏覽器動态實時重新整理功能(我個人了解的意思,如果大家想詳細了解,請百度之),這個是預設啟用的,也可以關閉這個功能,有了這個功能,在VS調試頁面時可以同時開多個浏覽器調試同一個頁面,而且修改這個頁面上前端代碼(也可能包含伺服器端代碼,沒具體使用這個功能)的同時,浏覽器實時顯示新修改好的效果。
代碼就不貼了,自己做個總結,以後遇到類似問題可供參考。