.net - 如何使用 VB.NET 从 FTP 下载目录

标签 .net vb.net visual-studio ftp ftpwebrequest

我正在尝试将多个目录从 FTP 服务器下载到我的本地计算机,

我已经尝试过了,

Const localFile As String = "C:\Documents and Settings\cr\Desktop\T\New Folder\"
Const remoteFile As String = "textbox.Text"
Const host As String = "ftp://ftp.example.com"
Const username As String = "username"
Const password As String = "password"

For i1 = 0 To ListBox1.SelectedItems.Count - 1
    Dim li As New ListViewItem
    li = ListView1.Items.Add(ListBox1.SelectedItems(i1))
    Dim URI1 As String = host + remoteFile & "/" & ListBox1.SelectedItems(i1)
    Dim ftp1 As System.Net.FtpWebRequest = CType(FtpWebRequest.Create(URI1), FtpWebRequest)
    ftp1.Credentials = New System.Net.NetworkCredential(username, password)
    ftp1.KeepAlive = False
    ftp1.UseBinary = True
    ftp1.Method = System.Net.WebRequestMethods.Ftp.DownloadFile
    Using response As System.Net.FtpWebResponse = CType(ftp1.GetResponse, System.Net.FtpWebResponse)
        Using responseStream As IO.Stream = response.GetResponseStream

            Dim length As Integer = response.ContentLength
            Dim bytes(length) As Byte

            'loop to read & write to file
            Using fs As New IO.FileStream(localFile & ListBox1.SelectedItems(i1), IO.FileMode.Create)
                Dim buffer(2047) As Byte
                Dim read As Integer = 1

                Do
                    read = responseStream.Read(buffer, 0, buffer.Length)
                    fs.Write(buffer, 0, read)

                Loop Until read = 0 'see Note(1)
                responseStream.Close()
                fs.Flush()
                fs.Close()
            End Using
            responseStream.Close()
        End Using

        response.Close()
    End Using
    li.BackColor = Color.Aquamarine
Next

但这里的问题是我可以从文件夹下载多个文件,但无法从主目录下载子目录及其内容。

基本上主目录由文件和子目录组成。那么有没有可能从 FTP 下载子目录及其内容?

提前致谢。

最佳答案

将我的答案翻译为 C# Download all files and subdirectories through FTP VB.NET:

FtpWebRequest 没有对递归文件下载(或任何其他递归操作)的任何明确支持。您必须自己实现递归:

  • 列出远程目录
  • 迭代条目、下载文件并递归到子目录(再次列出它们等)

一个棘手的部分是识别子目录中的文件。无法使用 FtpWebRequest 以可移植的方式做到这一点。不幸的是,FtpWebRequest 不支持 MLSD 命令,这是在 FTP 协议(protocol)中检索具有文件属性的目录列表的唯一可移植方法。另请参阅Checking if object on FTP server is file or directory .

您的选择是:

  • 对文件名执行的操作对于文件肯定会失败,而对于目录则成功(反之亦然)。 IE。你可以尝试下载“名字”。如果成功,它是一个文件,如果失败,它是一个目录。
  • 您可能很幸运,在您的具体情况下,您可以通过文件名区分文件和目录(即所有文件都有扩展名,而子目录没有)
  • 您使用长目录列表(LIST command = ListDirectoryDe​​tails 方法)并尝试解析特定于服务器的列表。许多 FTP 服务器使用 *nix 样式的列表,您可以通过条目开头的 d 来标识目录。但许多服务器使用不同的格式。以下示例使用此方法(假设为 *nix 格式)
Sub DownloadFtpDirectory(
        url As String, credentials As NetworkCredential, localPath As String)
    Dim listRequest As FtpWebRequest = WebRequest.Create(url)
    listRequest.Method = WebRequestMethods.Ftp.ListDirectoryDetails
    listRequest.Credentials = credentials

    Dim lines As List(Of String) = New List(Of String)

    Using listResponse As FtpWebResponse = listRequest.GetResponse(),
          listStream As Stream = listResponse.GetResponseStream(),
          listReader As StreamReader = New StreamReader(listStream)
        While Not listReader.EndOfStream
            lines.Add(listReader.ReadLine())
        End While
    End Using

    For Each line As String In lines
        Dim tokens As String() =
            line.Split(New Char() {" "}, 9, StringSplitOptions.RemoveEmptyEntries)
        Dim name As String = tokens(8)
        Dim permissions As String = tokens(0)

        Dim localFilePath As String = Path.Combine(localPath, name)
        Dim fileUrl As String = url + name

        If permissions(0) = "d" Then
            If Not Directory.Exists(localFilePath) Then
                Directory.CreateDirectory(localFilePath)
            End If
            DownloadFtpDirectory(fileUrl + "/", credentials, localFilePath)
        Else
            Dim downloadRequest As FtpWebRequest = WebRequest.Create(fileUrl)
            downloadRequest.Method = WebRequestMethods.Ftp.DownloadFile
            downloadRequest.Credentials = credentials

            Using downloadResponse As FtpWebResponse = downloadRequest.GetResponse(),
                  sourceStream As Stream = downloadResponse.GetResponseStream(),
                  targetStream As Stream = File.Create(localFilePath)
                Dim buffer As Byte() = New Byte(10240 - 1) {}
                Dim read As Integer
                Do
                    read = sourceStream.Read(buffer, 0, buffer.Length)
                    If read > 0 Then
                        targetStream.Write(buffer, 0, read)
                    End If
                Loop While read > 0
            End Using
        End If
    Next
End Sub

使用如下函数:

Dim credentials As NetworkCredential = New NetworkCredential("user", "mypassword")
Dim url As String = "ftp://ftp.example.com/directory/to/download/"
DownloadFtpDirectory(url, credentials, "C:\target\directory")
<小时/>

如果您想避免解析特定于服务器的目录列表格式的麻烦,请使用支持 MLSD 命令和/或解析各种 LIST 列表的第三方库格式;和递归下载。

例如 WinSCP .NET assembly您只需调用 Session.GetFiles 即可下载整个目录:

' Setup session options
Dim SessionOptions As SessionOptions = New SessionOptions
With SessionOptions
    .Protocol = Protocol.Ftp
    .HostName = "ftp.example.com"
    .UserName = "user"
    .Password = "mypassword"
End With

Using session As Session = New Session()
    ' Connect
    session.Open(SessionOptions)

    ' Download files
    session.GetFiles("/directory/to/download/*", "C:\target\directory\*").Check()
End Using

在内部,WinSCP 使用 MLSD 命令(如果服务器支持)。如果没有,它会使用 LIST 命令并支持数十种不同的列表格式。

Session.GetFiles method默认情况下是递归的。

(我是 WinSCP 的作者)

关于.net - 如何使用 VB.NET 从 FTP 下载目录,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22626766/

相关文章:

asp.net - RadControls for ASP.NET (telerik) 中的 OnClientClick 和 AJAX 不起作用

c++ - 如何从数组中删除一个元素,然后将其他元素移过来?

visual-studio - Visual Studio 中的 MSDN 链接

.net - 从 PHP 调用 .NET 类的最佳方式?

c# - PCL - 对于某些目标,无法从 Func<IThing<T>> 转换为 Func<Object>

vb.net - 我想在调用函数返回错误时控制错误消息

c++ - Visual Studio SFML 链接器错误

c# - 为什么我的对象不需要 ServiceKnownType?

c# - 如何使用 NUnit 模拟属性?

html - VB.net WebBrowser 控件不处理带有 float 的 CSS div