Die Community zu .NET und Classic VB.
Menü

Tipp-Upload: VB.NET 0277: asynchroner HTTP - / FTP - Download

 von 

Über den Tipp  

Dieser Tippvorschlag ist noch unbewertet.

Der Vorschlag ist in den folgenden Kategorien zu finden:

  • Internet und Netzwerke

Dem Tippvorschlag wurden folgende Schlüsselwörter zugeordnet:
HTTP ,FTP , Download,thread,asynchron

Der Vorschlag wurde erstellt am: 10.06.2008 23:53.
Die letzte Aktualisierung erfolgte am 24.05.2009 10:51.

Zurück zur Übersicht

Beschreibung  

Download von WebSites (per HTTP) und Files (per FTP)
sehr einfaches Threading

Schwierigkeitsgrad

Schwierigkeitsgrad 2

Verwendete API-Aufrufe:

Download:

Download des Beispielprojektes [14,96 KB]

' Dieser Source stammt von http://www.activevb.de
' und kann frei verwendet werden. Für eventuelle Schäden
' wird nicht gehaftet.

' Um Fehler oder Fragen zu klären, nutzen Sie bitte unser Forum.
' Ansonsten viel Spaß und Erfolg mit diesem Source!
'
' Beachten Sie, das vom Designer generierter Code hier ausgeblendet wird.
' In den Zip-Dateien ist er jedoch zu finden.

' ----------- Anfang Projektgruppe WebDownload.sln -----------
' ---------- Anfang Projektdatei WebDownload.vbproj ----------
' --------------- Anfang Datei CrossThreadX.vb ---------------
' IDE-Voreinstellungen:
' Option Strict On
' Option Explicit On
' Option Infer On

' Projekt-Voreinstellungen
' Imports System
' Imports System.Windows.Forms
' Imports System.Collections.Generic
' Imports Microsoft.VisualBasic.ControlChars

Imports System.Runtime.CompilerServices

Namespace System.Windows.Forms

    ''' <summary>
    ''' Stellt Methoden bereit, mit denen ein beliebiger Methoden-Aufruf mit bis zu 3 Argumenten
    ''' in einen Nebenthread verlegt werden kann, bzw. aus einem Nebenthread in den Hauptthread
    ''' </summary>
    Public Module CrossThreadX

        <Extension()> _
            Public Sub RunAsync(Of T1, T2, T3)( _
                  ByVal Action As Action(Of T1, T2, T3), _
                  ByVal Arg1 As T1, ByVal Arg2 As T2, ByVal Arg3 As T3)

            ' Aufruf von Action.EndInvoke() gewährleisten, indem er als Callback-Argument
            ' mitgegeben wird
            Action.BeginInvoke(Arg1, Arg2, Arg3, AddressOf Action.EndInvoke, Nothing)

        End Sub

        <Extension()> _
            Public Sub RunAsync(Of T1, T2)( _
                  ByVal Action As Action(Of T1, T2), ByVal Arg1 As T1, ByVal Arg2 As T2)

            Action.BeginInvoke(Arg1, Arg2, AddressOf Action.EndInvoke, Nothing)

        End Sub

        <Extension()> _
            Public Sub RunAsync(Of T1)(ByVal Action As Action(Of T1), ByVal Arg1 As T1)

            Action.BeginInvoke(Arg1, AddressOf Action.EndInvoke, Nothing)

        End Sub

        <Extension()> _
            Public Sub RunAsync(ByVal Action As Action)

            Action.BeginInvoke(AddressOf Action.EndInvoke, Nothing)

        End Sub

        Private Function GuiCrossInvoke( _
                  ByVal Action As [Delegate], ByVal ParamArray Args() As Object) As Boolean

            If Application.OpenForms.Count = 0 Then

                ' wenn kein Form mehr da ist, so tun, als ob das Invoking ausgeführt wäre
                Return True
            End If

            If Application.OpenForms(0).InvokeRequired Then
                Application.OpenForms(0).BeginInvoke(Action, Args)
                Return True
            End If

        End Function

        <Extension()> _
            Public Sub RunGui(Of T1, T2, T3)( _
                  ByVal Action As Action(Of T1, T2, T3), _
                  ByVal Arg1 As T1, ByVal Arg2 As T2, ByVal Arg3 As T3)

            If Not GuiCrossInvoke(Action, Arg1, Arg2, Arg3) Then Action(Arg1, Arg2, Arg3)

        End Sub

        <Extension()> _
            Public Sub RunGui(Of T1, T2)( _
                  ByVal Action As Action(Of T1, T2), ByVal Arg1 As T1, ByVal Arg2 As T2)

            If Not GuiCrossInvoke(Action, Arg1, Arg2) Then Action(Arg1, Arg2)

        End Sub

        <Extension()> _
            Public Sub RunGui(Of T1)(ByVal Action As Action(Of T1), ByVal Arg1 As T1)

            If Not GuiCrossInvoke(Action, Arg1) Then Action(Arg1)

        End Sub

        <Extension()> _
            Public Sub RunGui(ByVal Action As Action)

            If Not GuiCrossInvoke(Action) Then Action()

        End Sub

        <Extension()> _
            Public Sub RunGui(ByVal Action As Action(Of String), ByVal ParamArray args() As Object)

            Action.RunGui(String.Concat(args))

        End Sub

        <Extension()> Public Function CreateAsync(Of T1, T2)(ByVal Action As Action(Of T1, _
            T2)) As Action(Of T1, T2)

            Return AddressOf Action.RunAsync

        End Function

    End Module

End Namespace

' ---------------- Ende Datei CrossThreadX.vb ----------------
' -------------- Anfang Datei frmWebDownload.vb --------------
Imports System.Net
Imports System.IO
Imports System.Threading

Public Class frmWebDownload

    Private _DownloadDir As New DirectoryInfo("Downloads")

    ' Private _Download As Action(Of WebRequest, String) = AddressOf Download
    Private _Download As Action(Of WebRequest, String) = CreateAsync(Of WebRequest, String)( _
        AddressOf Download)

    Private _DisplayMessage As Action(Of String)

    Private Sub frmWebDownload_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load

        _DisplayMessage = AddressOf RichTextBox1.AppendText

        With _DownloadDir

            If .Exists Then .Delete(True)
            .Create()
        End With

        ' WebSites für HTTP-Download
        Me.cmbHTTP.DataSource = New String() { _
            "http://foren.activevb.de/cgi-bin/foren/list.pl?forum=6", _
            "http://www.activevb.de/cgi-bin/upload/upload.pl"}

        ' Downloads für FTP-Download
        Me.cmbFTP.DataSource = New String() { _
            "http://www.activevb.de/tipps/vbnetdownloads/tipp0018.zip", _
            "http://www.apiviewer.de/downloadsstore/apv2004_v310.exe"}

    End Sub

    Private Function InsertID(ByVal fullName As String) As String

        Static id As Integer = -1

        id += 1

        Dim ext = Path.GetExtension(fullName)
        Dim baseName = Path.GetFileNameWithoutExtension(fullName)

        Return Path.Combine(_DownloadDir.FullName, String.Concat(baseName, "_", id, ext))

    End Function

    Private Sub btHTTPDownload_Click(ByVal sender As Object, ByVal e As EventArgs) _
              Handles btHTTPDownload.Click

        Dim Src = cmbHTTP.SelectedItem.ToString

        _Download(HttpWebRequest.Create(Src), InsertID("Webpage.htm"))

    End Sub

    Private Sub btFileDownload_Click(ByVal sender As Object, ByVal e As EventArgs) _
              Handles btFileDownload.Click

        Dim Src = cmbFTP.SelectedItem.ToString

        _Download(FtpWebRequest.Create(Src), InsertID(Src))

    End Sub

    Private Sub Download(ByVal Request As WebRequest, ByVal Dest As String)

        ' HttpWebRequest und FtpWebRequest werden gleich behandelt
        Dim Filename = Path.GetFileName(Dest)

        _DisplayMessage.RunGui("Begin Download", Tab, Request.RequestUri.AbsoluteUri, " " & _
            "-> ", Filename, Lf)

        Using Response = Request.GetResponse(), Reader = Response.GetResponseStream(), Writer _
            = New IO.FileStream(Dest, FileMode.Create)

            Reader.WriteTo(Writer)
        End Using

        _DisplayMessage.RunGui("Download finished", Tab, Filename, Lf)

    End Sub

End Class

' --------------- Ende Datei frmWebDownload.vb ---------------
' ---------------- Anfang Datei modHelpers.vb ----------------
Imports System.Runtime.CompilerServices

Public Module modHelpers

    <Extension()> _
        Public Sub RunAsync(ByVal Action As [Delegate], ByVal ParamArray Args As Object())

        ' Einfaches "Zwischenschalten" der RunAsync-Methode verlagert die Action in einen
        ' eigenen Thread
        ' Nicht verwechseln mit der "Zurückverlagerung" durch Control.BeginInvoke(Delegate, Arg)!!
        With Action.GetType

            Dim Mtd = .GetMethod("BeginInvoke")

            Mtd.Invoke(Action, Args)
        End With

    End Sub

End Module

' ----------------- Ende Datei modHelpers.vb -----------------
' ----------------- Anfang Datei StreamX.vb  -----------------
Imports System.Runtime.CompilerServices
Imports System.IO

Public Module StreamX

    ''' <summary> kopiert von einem Stream in einen anderen </summary>
    ''' <remarks> 
    ''' Es gibt Streams ohne festgelegtes Ende (zB. NetworkStream).
    ''' In solchem Fall **muß** 'count' angegeben werden.
    ''' </remarks>
    <Extension()> _
        Public Sub WriteTo( _
              ByVal readStream As Stream, _
              ByVal writeStream As Stream, _
              Optional ByVal count As Long = -1, _
              Optional ByVal bufSize As Integer = Short.MaxValue)

        Dim buf(bufSize - 1) As Byte

        If count < 0 AndAlso readStream.CanSeek Then
            count = readStream.Length - readStream.Position
        End If

        If count < 0 Then

            ' Durch 0-Byte-Lesevorgang terminierte Kopier-Schleife
            ' Ein NetworkStream würde ein Timeout-Problem verursachen
            Do

                Dim portion = readStream.Read(buf, 0, bufSize)

                If portion = 0 Then Return
                writeStream.Write(buf, 0, portion)
            Loop

        Else

            ' zähler-gesteuerte Kopier-Schleife
            Do

                If count < bufSize Then bufSize = CInt(count)

                Dim portion = readStream.Read(buf, 0, bufSize)

                If portion = 0 Then Throw New ArgumentException("Die angegebene Anzahl " & _
                    "Bytes konnte nicht aus dem Lese-Stream gelesen werden", "readStream " & _
                    "+ count")

                count -= portion
                writeStream.Write(buf, 0, portion)
            Loop Until count = 0

        End If

    End Sub

End Module

' ------------------ Ende Datei StreamX.vb  ------------------
' ----------- Ende Projektdatei WebDownload.vbproj -----------
' ------------ Ende Projektgruppe WebDownload.sln ------------

	

Diskussion  

Diese Funktion ermöglicht es, Fragen, die die Veröffentlichung des Tipps betreffen, zu klären, oder Anregungen und Verbesserungsvorschläge einzubringen. Nach der Veröffentlichung des Tipps werden diese Beiträge nicht weiter verlinkt. Allgemeine Fragen zum Inhalt sollten daher hier nicht geklärt werden.

Um eine Diskussion eröffnen zu können, müssen sie angemeldet sein.