使用 Webclient 實現應用自動更新
本文提供了一個通過URL下載文件的簡易實現。
背景
很多應用都會提供一個自動更新的功能,這樣終端用戶能獲得最好的用戶體驗。簡單地說,自動更新可以通過兩步完成:
-
Step 1: 訪問程序的服務端來檢查是否有可用的新版本。
</li> -
Step 2: 詢問用戶是否要更新并遵從用戶的選擇。
</li> </ul>本文主要會講解圍繞上面兩步所展開的工作流程。這是我的實際工作經驗。如果您有什么疑問可以給我留言。
代碼
1.從服務器取得最新版本信息.
通常應用會有一個像1.20.11這樣的版本號,這是一個通過'.'分隔的一系列數字。當檢查服務器上的最新版本是否比用戶的當前版本新時,使用版本號是一個很不錯的方法。通常我們會認為1.20.22的版本要比1.20.11的版本新。
[Update]
CurrentVersion = 1.10.12
IniPath = ftp://192.168.29.19//Version.ini</pre>
此處有兩個數據行。"CurrentVersion"代表了當前(用戶)的應用版本,"IniPath"代表了服務器的Version.ini文件路徑。我們可以下載這個Version.ini文件并讀取它的版本號來做對比,這樣就可以檢查是否需要通知用戶(是否要更新應用)。
服務端的Version.ini內容:
[Update]
CurrentVersion = 1.11.00
ExePath = ............</pre>
第一個數據行表示了服務器上的應用的最新版本號,第二行代表了新版本應用的下載路徑。
稍后我們會介紹如何下載這個文件,現在我先介紹如何比較兩個版本號:
/** * * @param version1 * @param version2 * @return if version1 > version2, return 1, if equal, return 0, else return -1 */ public static int compare(String version1, String version2) { if (version1 == null || version1.Length == 0 || version2 == null || version2.Length == 0 ) { return -1; } int index1 = 0; int index2 = 0; while(index1 < version1.Length && index2 < version2.Length) { int[] number1 = getValue(version1, index1); int[] number2 = getValue(version2, index2); if (number1[0] < number2[0]) return -1; else if (number1[0] > number2[0]) return 1; else { index1 = number1[1] + 1; index2 = number2[1] + 1; } } if(index1 == version1.Length && index2 == version2.Length) return 0; if(index1 < version1.Length) return 1; else return -1; } /** * * @param version * @param index the starting point * @return the number between two dots, and the index of the dot */ public static int[] getValue(string version, int index) { int[] value_index = new int[2]; StringBuilder sb = new StringBuilder(); while(index < version.Length && version.ElementAt(index) != '.') { sb.Append(version.ElementAt(index)); index++; } value_index[0] = Convert.ToInt32(sb.ToString(),10); value_index[1] = index; return value_index; }
這段代碼通過'.'字符來分隔version字符串,然后對取得的數字依次進行比較.
(譯者注:
本人覺得這個方法太過繁瑣,如果使用android應用的版本控制會好點:定義一個versionName和versionCode,versionName是一個字符串,它作為版本的數字序列或名稱,而versionCode作為int型的版本號,查檢更新的時候可以通過versionCode這個int型的數字直接比較)
2. 從服務端下載文件.
正如本文標題所說,此方法會用到webclient接口來進行下載。它提供了很多用于下載的方法。
webclient提供了同步和異常方法。同步方法會阻塞線程。它通常用于下載耗時較短的小文件。在這我們使用同步方法來下載INI文件。代碼如下:
// New a webclient object WebClient web = new WebClient();
//Download synchronized
try { string UserName = AutoUpdate.Form1.GetStringFromFile("UpdateInfo", "ID_USER_NAME"); string PassWord = AutoUpdate.Form1.GetStringFromFile("UpdateInfo", "ID_USER_PASSWORD"); web.Credentials = new NetworkCredential(UserName, PassWord); web.DownloadFile(new Uri(ServerIniPath.ToString()), ItemSavePath); } catch (System.Exception ex) { throw ex; }</pre>
我們可以從INI文件中取得最新應用的下載路徑,如果需要下載它,我們應該使用異步方法.
try { web.DownloadFileAsync(new Uri(DownLoadAddress), ItemSavePath); web.DownloadProgressChanged += client_DownloadProgressChanged; web.DownloadFileCompleted += client_DownloadFileCompleted; } catch (System.Exception ex) { throw ex; }
從代碼我們可以看到當調用下載方法后,我們也為它添加了兩個事件handler(處理器)。這兩個handler可以告知我們下載的詳細信息,例如已下載百分比,已下載字節數等等。handler代碼如下:
void client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e) { Action<AsyncCompletedEventArgs> onCompleted = progressCompleted; onCompleted.Invoke(e); }
void client_DownloadFileProgresschanged(object sender, AsyncCompletedEventArgs e) { Action<DownloadProgressChangedEventArgs> onCompleted = progressChanging; onCompleted.Invoke(e); }</pre>
方法progressCompleted和progressChanging可以按需實現。
3. 下載異常.
這個異常并不是通過try和catch捕獲的異常。這是一個真正的異常:網絡不可用,用戶取消更新,服務器不可用。因為我們使用異步方法進行下載,這會導致很多并發問題。接下來我會介紹我是如何處理它們的。
網絡不可用.
這個問題隨時都會發生,所以我需要啟用另一個線程,它會根據時間來檢查網絡狀態。一旦發現網絡斷開了,我會調用下載線程來通知用戶。
如何檢查網絡狀態其實很簡單,我們可以使用 "wininet.dll"提供的方法。
[DllImport("wininet.dll")] public static extern bool InternetGetConnectedState(out int lpdwFlags, int dwReserved);
用戶取消下載:
weblient已經考濾到了這種情況,它提供了cancel方法來停止下載和dispose方法釋放下載進程占用的資源。
結束.