[VB.net] 如何實現「非同步下載」檔案?(二)從 FTP 下載時如何先得到 File Size。

前文提到使用 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 取得檔案長度的手法。

  1. 先從 VS.net 的現有類別下手,在 System.Net 命名空間有個 FtpWebRequest 類別,顧名思義應該是向 FTP 發出要求的可用物件。
  2. 另外在 FtpWebRequest 裡面有一個方法叫做 GetResponse(),看起來正是我要的東西。
  3. 查看了這兩個物件的用法,試寫了一段程式碼:

        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
    

  4. 測試結果的確找到了檔案的長度。

    image
     
  5. 接下來改寫一下表單程式碼,加上這個 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
    


  6. 結果都正常了。

    image
     

到此雖然主要問題解決了,不過仍有些小問題在。

  1. 因為從 FTP Server 下載要先花時間等候 Response() 會使下載時間變長了,尤其是當 WebClient 去執行  DownLoadFile 時又要再 Request 一次。
  2. 用下載微軟的 ftp://ftp.microsoft.com/bussys/sql/ODBCUTIL.ZIP 檔案為例,按下 Button 約4秒才看到進度,開始後約1秒就下載完成了。這對下載小檔而言是很傷時間的。
  3. 可能是 FtpWebResponse() 對查詢檔案的長度的任務而言是太牛刀了些吧。

 

就先這樣囉,下回要貼的是如何使用更輕小的 Socket 物件,使用 Row Command 和 FTP 進行交握動作取得檔案大小,甚至就直接建立 NetworkStream 逕行下載檔案了。

 

專案原始檔下載
非同步下載_02_取得_FileSize.rar

demo 執行檔下載
非同步下載_02_demo.rar

 

 

 


ku3