Code zu Internet und Netzwerk in .NET
- Laden von Dateien aus dem Internet
- Ermitteln der eigenen IP-Adresse
- Wiederverwenden von Internet-Explorer-Fenstern
- Herstellen und Beenden der Standardinternetverbindung
- Laden einer kennwortgeschützten Ressource unter Weitergabe von Cookies
- Auswahl sinnvoller Dateinamen für heruntergeladene Dateien
Laden von Dateien aus dem Internet
Zum Laden von Dateien aus dem Internet stehen im .NET Framework mehrere Möglichkeiten zur Verfügung. Eine sehr einfache Lösung findet sich mit der Klasse WebRequest
, mittels derer eine Anfrage gestellt und die Antwort ausgewertet werden kann. Damit das nachstehende Beispiel funktioniert, müssen die Namensräume System.IO
und System.Net
importiert werden:
Public Function LoadTextFile(ByVal Url As String) As String
Dim Request As WebRequest = WebRequest.Create(Url)
Dim Response As WebResponse = Request.GetResponse()
Dim Reader As New StreamReader(Response.GetResponseStream())
LoadTextFile = Reader.ReadToEnd()
Reader.Close()
Response.Close()
Return LoadTextFile
End Function
WebRequest
und WebResponse
.Soll der Inhalt der geladenen Datei nicht direkt in der Anwendung weiterverarbeitet werden, sondern in eine Datei geschrieben werden, bietet sich die Verwendung von WebClient.DownloadFile
an. Die hier demonstrierten Methoden arbeiten synchron, weshalb der Thread, in dem sie ausgeführt werden, blockiert wird. Deshalb ist es in Fällen, in denen asynchrones Verhalten gewünscht wird, sinnvoll, bei Verwendung von WebRequest
die Methoden BeginGetResponse
und EndGetResponse
zu benutzen oder das Herunterladen in einen eigenen Thread zu verfrachten.
Christoph Schneegans [MVP] hat festgestellt, daß die beschriebene Methode nur bei Ressourcen funktioniert, die UTF-8-codiert sind. Bei anders codierten Ressourcen kann man unter .NET 2.0 das WebRequest
-Objekt in den Typ HttpWebRequest
umwandeln und über dessen Eigenschaft CharSet
den Zeichensatz bestimmen, mit dem die Ressource codiert ist, um diesen anschließend beim Lesen des Antwortstroms anzugeben. Allerdings ist der Wert dieser Eigenschaft bei .NET 1.1 anscheinend immer eine leere Zeichenfolge.
Ermitteln der eigenen IP-Adresse
Folgender Code ermittelt die IP-Adresse des Systems, auf dem die Anwendung, die den Code enthält, ausgeführt wird. Es müssen die Namensräume System.Windows.Forms
und System.Net
importiert werden, damit der Code kompiliert werden kann:
Console.WriteLine("IP-Adresse(n) des Computers:")
For Each Address As IPAddress In Dns.Resolve(SystemInformation.ComputerName).AddressList
Console.WriteLine(Address.ToString())
Next Address
Wiederverwenden von Internet-Explorer-Fenstern
Manchmal ist es erforderlich, daß die Navigation innerhalb eines oder mehrerer bestehender Fenster des Webbrowsers stattfindet. Eine allgemeine Lösung für alle Webbrowser existiert aufgrund unterschiedlicher Konfigurationsmöglichkeiten der einzelnen Webbrowser nicht. Ist jedoch nur der Internet Explorer im Einsatz, kann zu diesem Zweck die im folgenden Listing angegebene Funktion ReplaceInternetExplorerUrl
benutzt werden. Bei Aufruf der Funktion wird wahlweise in einem oder mehreren Fenstern, in denen eine Ressource mit einer bestimmten Adresse angezeigt wird, zu einer anderen Adresse navigiert und die Anzahl der wiederverwendeten Fenster zurückgegeben. Um das Beispiel kompilieren zu können, ist es erforderlich, im Projekt einen Verweis auf Microsoft Internet Controls (Datei SHDocVw.dll
) aufzunehmen und den Namensraum SHDocVw
zu importieren:
' Requires a reference to "Microsoft Internet Controls" ("SHDocVw.dll").
Public Function ReplaceInternetExplorerUrl( _
ByVal OldUrl As String, _
ByVal NewUrl As String, _
Optional ByVal FirstInstanceOnly As Boolean = False _
) As Integer
Dim ReplacedCount As Integer
For Each IE As InternetExplorer In New ShellWindows()
If IE.LocationURL = OldUrl Then
IE.Navigate(NewUrl)
ReplacedCount += 1
If FirstInstanceOnly Then
Exit For
End If
End If
Next IE
Return ReplacedCount
End Function
Herstellen und Beenden der Standardinternetverbindung
.NET 1.1 bietet keine Unterstützung zum Wählen von Internetverbindungen auf dem lokalen Rechner. Derartige Funktionalität läßt sich jedoch leicht mittels der Plattformfunktionen InternetAutodial
und InternetAutodialHangup
aus der WinInet.dll
auch in .NET-basierenden Anwendungen nutzen. Die im nachstehenden Listing angegebene Klasse InternetDial
verfügt über gemeinsame Methoden zum Herstellen und Beenden der Standardinternetverbindung und stellt alle Möglichkeiten, welche die dahinterliegenden Plattformfunktionen bieten, zur Verfügung. Soll etwa die Standardverbindung unter Anzeige des Einwahldialogs hergestellt werden, kann der Aufruf InternetDialer.Dialup(InternetDialer.AutoDialOptions.ForceOnline)
benutzt werden:
Public Class InternetDialer
Public Declare Function InternetAutodial Lib "wininet.dll" ( _
ByVal dwFlags As Int32, _
ByVal hwndParent As IntPtr _
) As Boolean
Private Const INTERNET_AUTODIAL_FORCE_ONLINE As Int32 = &H1
Private Const INTERNET_AUTODIAL_FORCE_UNATTENDED As Int32 = &H2
Private Const INTERNET_AUTODIAL_FAILIFSECURITYCHECK As Int32 = &H4
Private Const INTERNET_AUTODIAL_OVERRIDE_NET_PRESENT As Int32 = &H8
Private Declare Function InternetAutodialHangup Lib "wininet.dll" ( _
ByVal dwReserved As Int32 _
) As Boolean
Public Enum AutoDialOptions
ForceOnline = INTERNET_AUTODIAL_FORCE_ONLINE
ForceUnattended = INTERNET_AUTODIAL_FORCE_UNATTENDED
FailIfSecurityCheck = INTERNET_AUTODIAL_FAILIFSECURITYCHECK
OverrideNetPresent = INTERNET_AUTODIAL_OVERRIDE_NET_PRESENT
End Enum
Public Shared Sub Dialup(ByVal Options As AutoDialOptions)
Dialup(Options, IntPtr.Zero)
End Sub
Public Shared Sub Dialup( _
ByVal Options As AutoDialOptions, _
ByVal Parent As Control _
)
Dialup(Options, Parent.Handle)
End Sub
Public Shared Sub Dialup( _
ByVal Options As AutoDialOptions, _
ByVal hwndParent As IntPtr _
)
If Not InternetAutodial(Options, hwndParent) Then
Throw _
New ApplicationException( _
"Error dialling the default internet connection." _
)
End If
End Sub
Public Shared Sub Hangup()
If Not InternetAutodialHangup(0) Then
Throw _
New ApplicationException( _
"Error disconnecting internet connection." _
)
End If
End Sub
End Class
InternetAutodial
und InternetAutodialHangup
.Laden einer kennwortgeschützten Ressource unter Weitergabe von Cookies
Mitglieder von Google AdSense können Berichte über den durch Werbung erzielten Umsatz mit einem Webbrowser abrufen. Zu diesem Zweck erfolgt eine POST
-Authentifizierung durch Übermittlung der Zugangsdaten über ein Webformular. Das serverseitig laufende Anmeldungsskript gibt einen Set-Cookie
-Kopfeintrag zurück. Dieses Cookie muß bei weiteren Anfragen weitergegeben werden. Während Webbrowser das automatisch erledigen, muß dies bei der Implementierung einer automatischen Berichtsabfrage berücksichtigt werden.
Die beiden im Folgenden präsentierten Ansätze wurden auf den Spezialfall von Google-AdSense-Berichten zugeschnitten, sodaß Codierungen fest im Code angegeben wurden. Zudem wurde auf den Aufruf des Abmeldeskripts verzichtet. Damit die Codebeispiele kompiliert werden können, muß der Namensraum System.Net
importiert werden. Das folgende Beispiel benötigt zudem den Namensraum System.Text
:
Dim Client As New WebClient()
Client.Headers.Add("Content-Type", "application/x-www-form-urlencoded")
Client.UploadData( _
"https://www.google.com/adsense/login.do", _
"POST", _
Encoding.UTF8.GetBytes("username=<user name>&password=<password>") _
)
Client.Headers.Clear()
Client.Headers.Add("Cookie", Client.ResponseHeaders("Set-Cookie"))
Dim Data() As Byte = _
Client.DownloadData("https://www.google.com/adsense/report/overview")
Client.Dispose()
MsgBox(Encoding.UTF8.GetString(Data))
Eine weitere Lösung läßt sich mit der Klasse CookieContainer
in Verbindung mit der Klasse HttpWebRequest
finden:
Dim Request As HttpWebRequest = _
DirectCast( _
HttpWebRequest.Create("https://www.google.com/adsense/login.do"), _
HttpWebRequest _
)
Dim Cookies As New CookieContainer()
With Request
.CookieContainer = Cookies
.Method = "POST"
.ContentType = "application/x-www-form-urlencoded"
End With
Dim Writer As New StreamWriter(Request.GetRequestStream())
Writer.Write("username=<user name>&password=<password>")
Writer.Close()
Request.GetResponse().Close()
Request = _
DirectCast( _
WebRequest.Create("https://www.google.com/adsense/report/overview"), _
HttpWebRequest _
)
Request.CookieContainer = Cookies
Dim Reader As New StreamReader(Request.GetResponse().GetResponseStream())
MsgBox(Reader.ReadToEnd())
Reader.Close()
CookieContainer
.Auswahl sinnvoller Dateinamen für heruntergeladene Dateien
Will man in einem Webbrowser eine Datei herunterladen oder die gerade angezeigte Webseite speichern, schlägt der Webbrowser einen mehr oder weniger sinnvollen Dateinamen für die lokale Datei vor. Wenn die Adresse der herunterzuladenden Ressource bereits einen sinnvollen Dateinamen enthält, kann dieser auch für die lokale Datei herangezogen werden. Schwieriger ist die Wahl eines passenden Dateinamens bei Adressen wie https://www.example.org/reports/2007/
und https://www.example.org/reports/?id=982385
.
Mögliche Ansatzpunkte für die Auswahl eines sinnvollen Dateinamens:
Der
Content-Disposition
-Kopfeintrag der vom Server gesendeten Antwort kann im Parameterfilename
den Dateinamen der Ressource enthalten.Der
Content-Type
-Kopfeintrag der Antwort kann zur Bestimmung der dem Inhaltstyp zugeordneten Dateinamenserweiterung über die MIME-Tabelle des Windows-Systems herangezogen werden.Segmente der Adresse (Dateiname, Verzeichnisname, Hostname, Anfragezeichenfolge) können zur Bildung des Dateinamens verwendet werden.
Die im nächsten Listing angegebene Methode DownloadFile
ist als Lösungsskizze zu verstehen. Plausibilitätsprüfungen für Parameter und ermittelte Werte wurden teilweise weggelassen. Die Methode geht bei der Auswahl eines sinnvollen Dateinamens für die heruntergeladene Datei folgendermaßen vor:
Ist der Parameter
PreferContentDisposition
gesetzt, wird ein allfällig vorhandenerContent-Disposition
-Kopfeintrag hinsichtlich einer Angabe des Dateinamens untersucht und ggf. dessen Wert als Dateiname herangezogen. Auf die Implementierung einer Plausibilitätsprüfung des ermittelten Dateinamens wurde verzichtet.Falls der Parameter
PreferContentDisposition
nicht gesetzt ist oder kein entsprechender Eintrag im Antwortkopf vorhanden war, wird der Datei-, Verzeichnis- oder Hostname aus der Adresse der Ressource zur Benennung herangezogen. Als Dateinamenserweiterung wird dabei die am System dem Inhaltstyp zugeordnete Erweiterung verwendet.
Imports Microsoft.Win32
Imports System.IO
Imports System.Net
⋮
Public Function DownloadFile( _
ByVal Uri As String, _
ByVal DestinationDirectory As String, _
Optional ByVal PreferContentDisposition As Boolean = False _
) As String
Dim Request As HttpWebRequest = _
DirectCast(WebRequest.Create(Uri), HttpWebRequest)
Dim Response As WebResponse = Request.GetResponse()
Dim ResponseStream As Stream = Response.GetResponseStream()
Dim FileName As String = Nothing
If PreferContentDisposition Then
Dim ContentDisposition As String = _
Response.Headers("Content-Disposition")
If ContentDisposition IsNot Nothing Then
Dim Parts() As String = Split(ContentDisposition, ";")
If Parts.Length > 0 Then
For i As Integer = 1 To Parts.Length
Dim DispositionParam As String = Parts(i)
Dim ParamParts() As String = _
Split(DispositionParam, "=")
Dim Param As String = ParamParts(0)
Dim ParamValue As String = ParamParts(1)
If Param Is "filename" Then
FileName = Path.GetFileName(ParamValue)
Exit For
End If
Next i
End If
End If
End If
If FileName Is Nothing Then
Dim FileUri As New Uri(Uri)
FileName = _
Replace( _
FileUri.Segments(FileUri.Segments.Length - 1), _
"/", _
String.Empty _
)
FileName = Path.GetFileNameWithoutExtension(FileName)
If Len(FileName) = 0 Then
FileName = Replace(FileUri.Host, ".", "_")
End If
Dim Key As RegistryKey = _
Registry.ClassesRoot.OpenSubKey( _
"MIME\Database\Content Type\" & Response.ContentType _
)
If Key IsNot Nothing Then
Dim Extension As String = _
DirectCast(Key.GetValue("Extension"), String)
If Extension IsNot Nothing Then
FileName = FileName & Extension
End If
End If
End If
FileName = Path.Combine(DestinationDirectory, FileName)
' Download data and write file.
Using Reader As New BinaryReader(ResponseStream)
Using Writer As New BinaryWriter( _
New FileStream(FileName, FileMode.CreateNew) _
)
' Read blocks of 4 KB.
Const BlockSize As Integer = 4096
Dim Buffer() As Byte
Do
Buffer = Reader.ReadBytes(BlockSize)
Writer.Write(Buffer)
Loop While Buffer.Length > 0
End Using
End Using
Response.Close()
Return FileName
End Function