前文提到使用 System.Net.WebClient 類別的 DownloadFileAsync() 方法從 FTP Server 下載檔案時,由於事先沒有該檔案的長度資料,以致在需要用到 ProgressBar 時呈現進度會有不便之處。
接下來就要分享一下從 FTP 取得檔案長度的手法。
1.先從 VS.net 的現有類別下手,在 System.Net 命名空間有個 FtpWebRequest 類別,顧名思義應該是向 FTP 發出要求的可用物件。
2.另外在 FtpWebRequest 裡面有一個方法叫做 GetResponse(),看起來正是我要的東西。
3.查看了這兩個物件的用法,試寫了一段程式碼:
前文提到使用 System.Net.WebClient 類別的 DownloadFileAsync() 方法從 FTP Server 下載檔案時,由於事先沒有該檔案的長度資料,以致在需要用到 ProgressBar 時呈現進度會有不便之處。接下來就要分享一下從 FTP 取得檔案長度的手法。
- 先從 VS.net 的現有類別下手,在 System.Net 命名空間有個 FtpWebRequest 類別,顧名思義應該是向 FTP 發出要求的可用物件。
- 另外在 FtpWebRequest 裡面有一個方法叫做 GetResponse(),看起來正是我要的東西。
-
查看了這兩個物件的用法,試寫了一段程式碼:
Dim 下載檔案大小 As Long = 0 Private Sub btn_下載1_Click() Handles btn_下載1.Click 下載檔案大小 = FtpGetFileSize(txt_下載檔名1.Text) End Sub '---取得檔案大小--- Function FtpGetFileSize(下載檔案及位置 As String) As Long Dim Ftp要求 As FtpWebRequest = WebRequest.Create(New Uri(下載檔案及位置)) With Ftp要求 .Method = WebRequestMethods.Ftp.GetFileSize .Credentials = New NetworkCredential("anonymous", "a@a") .UseBinary = True .UsePassive = True End With Dim ftp回應 As FtpWebResponse = Ftp要求.GetResponse() Return ftp回應.ContentLength End Function
-
測試結果的確找到了檔案的長度。
-
接下來改寫一下表單程式碼,加上這個 FtpGetFileSize() 函式再試向 FTP Server 下載看看。
Imports System.Net Imports System.IO Public Class Form1 Private WithEvents 檔案下載器_1 As New System.Net.WebClient Dim 下載檔案大小 As Long = 0 '---下載並存檔--- Private Sub DownLoadFile() Handles btn_DownLoadFile.Click 下載檔案大小 = 0 Dim URI = New Uri(TextBox1.Text) If URI.Scheme = URI.UriSchemeFtp Then '---若是向 FTP 下載才要先取回檔案長度--- 下載檔案大小 = FtpGetFileSize(URI.OriginalString) End If If Not 檔案下載器_1.IsBusy Then 檔案下載器_1.DownloadFileAsync(URI, TextBox2.Text) End Sub '---當下載 Progress 改變--- Private Sub P1(sender As Object, e As System.Net.DownloadProgressChangedEventArgs) Handles 檔案下載器_1.DownloadProgressChanged Dim TotalBytes As Long = If(e.TotalBytesToReceive <= 0, 下載檔案大小, e.TotalBytesToReceive) Dim PercenTage As Integer = e.BytesReceived / TotalBytes * 100 Me.Text = e.BytesReceived & " -- " & PercenTage & "%" End Sub '---下載 File 完成--- Private Sub DownloadFileCompleted(sender As Object, e As System.ComponentModel.AsyncCompletedEventArgs) Handles 檔案下載器_1.DownloadFileCompleted 檔案下載器_1.Dispose() If e.Cancelled Then MsgBox("使用者中斷了下載作業!") : Exit Sub If Not IsNothing(e.Error) Then MsgBox("下載失敗!") : Exit Sub MsgBox("下載成功。") End Sub '---使用者放棄鍵--- Private Sub btn_Canel_1_Click(sender As System.Object, e As System.EventArgs) Handles btn_Canel.Click 檔案下載器_1.CancelAsync() End Sub '---取得檔案大小--- Function FtpGetFileSize(下載檔案及位置 As String) As Long Dim Ftp要求 As FtpWebRequest = FtpWebRequest.Create(New Uri(下載檔案及位置)) With Ftp要求 .Method = WebRequestMethods.Ftp.GetFileSize .Credentials = New NetworkCredential("anonymous", "a@a") .UseBinary = True .UsePassive = True End With Dim ftp回應 As FtpWebResponse = Ftp要求.GetResponse() Return ftp回應.ContentLength End Function End Class
-
結果都正常了。
到此雖然主要問題解決了,不過仍有些小問題在。
- 因為從 FTP Server 下載要先花時間等候 Response() 會使下載時間變長了,尤其是當 WebClient 去執行 DownLoadFile 時又要再 Request 一次。
- 用下載微軟的 ftp://ftp.microsoft.com/bussys/sql/ODBCUTIL.ZIP 檔案為例,按下 Button 約4秒才看到進度,開始後約1秒就下載完成了。這對下載小檔而言是很傷時間的。
- 可能是 FtpWebResponse() 對查詢檔案的長度的任務而言是太牛刀了些吧。
就先這樣囉,下回要貼的是如何使用更輕小的 Socket 物件,使用 Row Command 和 FTP 進行交握動作取得檔案大小,甚至就直接建立 NetworkStream 逕行下載檔案了。
專案原始檔下載
[非同步下載_02_取得_FileSize.rar]
demo 執行檔下載
[非同步下載_02_demo.rar]