Dateiupload in eine Datenbank
Autor: Jürgen Gutsch
Version: 1.0
Sprache: vb.net
Benötigt wird:
.net Framework ab v1.x.xxxx
Odbc.net
VisualStudio.net
Und eine beliebige Datenbank mit dazugehörendem Odbc-Treiber
Die Scripts wurden mit folgenden Datenbanken getestet:
mssql-Server 2000 (Odbc-Treiber: SQL Server v 2000)
mySQL v4.0.0 Alpha-NT (Odbc-Treiber: myODBC v 2.50.39.00)
Access 2000 (Odbc-Treiber: Microsoft Access Driver 4.00.6019.00)
Getestet auf winXP-Professional (IIS 5.1)
Upload in eine Datenbank
Die Datenbank
Die Schwierigkeit beim Datenbankupload besteht nicht nur unbedingt darin, die Datei auf den Server zu laden und weiter zu verarbeiten, sondern eher darin, die hochgeladene Datei in die Datenbank zu bekommen. Mit einfachem SQL kommt man da nicht weit, wie ich feststellen musste. Bauen wir uns ersteinmal die Datenbank zusammen (für die Beispiele hier benutze ich den SQL Server, in der Downloadfile ist allerdings noch ein SQL-Dump für mySQL und eine Access Datenbank enthalten). Zuerst muss eine neue Datenbank angelegt werden). Die Tabelle "documents" sieht folgendermaßen aus:
id [numeric](20, 0) IDENTITY (1, 1) NOT NULL , contenttype [varchar] (50) NOT NULL , contentlength [numeric](20, 0) NOT NULL , filename [varchar] (150) NOT NULL , filecontent [image] NOT NULL
Man beachte, dass die Spalte filecontent von Typ image ist und nicht binary. binary hat den Nachteil, dass immer 8.000 Bytes in der Datenbank reserviert werden, und auch nicht mehr als 8.000 Bytes aufnehmen kann. Auch varbinary kann nur maximal 8.000 Bytes aufnehmen. image dagegen ist ein variabler Datentyp, der bis zu 231-1 Bytes fassen kann (2,147,483,647 Bytes)
(Anmerkung: für Access wird der Typ OLE-Objekt verwendet, und für mySQL der Typ Blob (65536 Bytes), ansonsten unterscheiden sich die Tabellen nicht sehr, in den jeweiligen Datenbanken)
Das Formular
Legen wir nun eine aspx-Seite "upload.aspx" im VisualStudio an.
Sie sollte jetzt etwa so aussehen:
<%@ Page Language="vb" AutoEventWireup="false"
Codebehind="upload.aspx.vb" Inherits="tutorials.upload"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>WebForm1</title>
<meta name="GENERATOR" content="Microsoft Visual Studio.NET 7.0">
<meta name="CODE_LANGUAGE" content="Visual Basic 7.0">
<meta name="vs_defaultClientScript" content="JavaScript">
<meta name="vs_targetSchema"
content="http://schemas.microsoft.com/intellisense/ie5">
</head>
<body>
<form id="Form1" method="post" runat="server"></form>
</body>
</html>
Das Form-Tag ergänzen wir um ein Attribut:
enctype="multipart/form-data"
Um auch Binärdaten zu senden.
Zwischen die Form-Tags setzen wir die benötigten Steuerelemente, so dass die aspx-Seite jetzt etwa so aussieht:
<%@ Page Language="vb" AutoEventWireup="false"
Codebehind="upload.aspx.vb" Inherits="tutorials.upload"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>WebForm1</title>
<meta name="GENERATOR" content="Microsoft Visual Studio.NET 7.0">
<meta name="CODE_LANGUAGE" content="Visual Basic 7.0">
<meta name="vs_defaultClientScript" content="JavaScript">
<meta name="vs_targetSchema"
content="http://schemas.microsoft.com/intellisense/ie5">
</head>
<body>
<form id="Form1" method="post" runat="server"
enctype="multipart/form-data">
<input type="file" name="file1" id="file1" runat="server" /><br />
<input type="file" name="file2" id="file2" runat="server" /><br />
<input type="file" name="file3" id="file3" runat="server" /><br />
<input type="file" name="file4" id="file4" runat="server" /><br />
<input type="file" name="file5" id="file5" runat="server" /><br />
<input type="file" name="file6" id="file6" runat="server" /><br />
<input type="submit" name="send" id="send"
runat="server" value="submit" />
<br />
<asp:label id="Label1" runat="server"></asp:label>
</form>
</body>
</html>
Damit währe unser Testformular vollständig.
Die Codebehind-Klasse
Um in die Codeansicht zu wechseln, klicken wir mit der rechten Maustaste in auf die aspx-Seite und wählen "Code anzeigen".
Folgender Code sollte vom VisualStudio angelegt worden sein:
Public Class upload Inherits System.Web.UI.Page Protected WithEvents file1 As _ System.Web.UI.HtmlControls.HtmlInputFile Protected WithEvents file2 As _ System.Web.UI.HtmlControls.HtmlInputFile Protected WithEvents file3 As _ System.Web.UI.HtmlControls.HtmlInputFile Protected WithEvents file4 As _ System.Web.UI.HtmlControls.HtmlInputFile Protected WithEvents file5 As _ System.Web.UI.HtmlControls.HtmlInputFile Protected WithEvents file6 As _ System.Web.UI.HtmlControls.HtmlInputFile Protected WithEvents Label1 As _ ystem.Web.UI.WebControls.Label Protected WithEvents send As _ System.Web.UI.HtmlControls.HtmlInputButton #Region " Web Form Designer Generated Code " 'This call is required by the Web Form Designer. <System.Diagnostics.DebuggerStepThrough()> _ Private Sub InitializeComponent() End Sub Private Sub Page_Init(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Init 'CODEGEN: This method call is required 'by the Web Form Designer 'Do not modify it using the code editor. InitializeComponent() End Sub #End Region Private Sub Page_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load 'Put user code to initialize the page here End Sub End Class
Um die Datenbanken per Odbc anzusprechen, müssen wir dem Projekt noch eine Referenz auf Odbc.net hinzufügen:
Menü: "Projekt" => "Referenz..."
Oder Rechtsklick auf den Referenzordner im Projektexplorer: "Referenz hinzufügen..."
Wählen Sie in dem sich öffnendem Fenster "Microsoft.Data.Odbc.dll"; aus, klicken Sie anschließend auf "Auswählen"; und dann auf "OK";.
(Anmerkung: Sollten Sie nur mit dem SQL Server arbeiten, brauchen Sie die Referenz nicht einzubinden, das gleiche gilt, wenn Sie nur Access benutzen:
Für den SQL Server ersetzen sie einfach alles was hier im Tutorial mit "Microsoft.Data.Odbc.Odbc..."; anfängt, mit "System.Data.SqlCient.Sql...". Für Access Ersetzen Sie "Microsoft.Data.Odbc.Odbc..."; mit "System.Data.Oledb.Oledb...";
Die ConnectionStrings müssen natürlich dementsprechend angepasst werden)
Nun sollten Sie noch die Odbc-Verbindungen zu den Datenbanken einrichten.
Ist das geschehen, können wir uns daran machen, dem Code eine neue Funktion hinzu zufügen:
Private Sub saveInDB() Dim strConn1 As String = "DSN=tut_sql;UID=user;PWD=passwd" Dim strConn2 As String = "DSN=tut_mysql;UID=user;PWD=passwd" Dim strConn3 As String = "DSN=tut_access;UID=user;PWD=passwd"
Hier habe ich drei ConnectionStrings angelegt, für meine drei Testdatenbanken.
Anschließend wird ein Objekt deklariert, dass alle gesendeten Dateien empfängt:
Dim FileCollection As System.Web.HttpFileCollection = Request.Files
Folgende Deklarationen werden noch benötigt:
Der Zähler für eine For-Schleife
Dim i As Integer
Die Dateieigenschaften werden hier zwischengespeichert
Dim ContentLength As Long Dim FileName As String Dim ContentType As String
noch ein Stream, der die Daten empfängt:
Dim IOStream As System.IO.Stream
Und noch ein Zähler, der für Ausgabe benötigt wird
Dim Counter As Integer
Mit FileCollection.Count bekommen wir die Anzahl der File-Felder, also auch die Anzahl der möglichen hochgeladenen Dateien, die in einer For-Schleife durchgearbeitet werden:
For i = 0 To FileCollection.Count - 1
Füllen wir nun den IOStream mit einer Datei, hierbei dient der Zähler des Schleife auch als Index der FileCollection:
IOStream = FileCollection(i).InputStream
Die InputStream-Eigenschaft, liefert uns einen BinaryStream, der die aktuelle Datei enthält.
So bekommen wir die Größe der Datei in Bytes, die wir hier mehramls brauchen werden
ContentLength = FileCollection(i).ContentLength
Nun wird ein Array vom Typ Byte deklariert ("FileContent"), der die Datei nachher aufnehmen soll.
Als Dimension wird die Dokumentgröße angegeben IOStream.Read ließt uns anschließend die Daten in das übergebene Array, der zweite Parameter enthält, ab welcher Position des Arrays die Daten eingefügt werden sollen und der Dritte, die Länge der Daten:
Dim FileContent(ContentLength) As Byte Dim status As Integer status = IOStream.Read(FileContent, 0, ContentLength)
Die Funktion Read() gibt uns die Länge des Streams wieder, wenn also nichts empfangen wurde, erhalten wir 0, was uns hilft die weitere Verarbeitung zu überspringen, um fehler zu vermeiden, falls keine Daten vorhanden sind:
If status > 0 Then Counter += 1
Wenn also eine Datei im Stream enthalten ist, ist der Status auf jeden Fall größer als 0 und wir können mit der Verarbeitung fortfahren und unseren Dateienzähler "Counter"; um eins erhöhen.
Die Eigenschaft FilenName liefert uns den gesamten String, der im File-Feld zu sehen war, leider wird der uns sehr wenig bringen. Um nur den Dateinamen zu erhalten spiele ich hier mit den neuen String-Funktionen von vb.net herum:
FileName = FileCollection(i).FileName
FileName = FileName.Substring(FileName.LastIndexOf("\"), _
(FileName.Length - FileName.LastIndexOf("\")))
FileName = FileName.Substring(1, FileName.Length - 1)
Für eine Spätere Verarbeitung der Datei, sollten wir auch noch den ContentType der Datei Speichern:
ContentType = FileCollection(i).ContentType
So nun bauen wir unsere Datenbankverbindung zusammen. Ganz wichtig ist hierbei der CommandBuilder, ohne den geht gar nichts. Er sorgt dafür, das nachher ein SQL-String zusammengebaut wird, der für ein Update der Datenbank sorgt:
Dim Connection As New Microsoft.Data.Odbc.OdbcConnection(strConn1) Dim strSQL As String = "SELECT " & _ "contenttype, contentlength, " & _ "filename, filecontent FROM documents" Dim Command As _ New Microsoft.Data.Odbc.OdbcCommand(strSQL, Connection) Dim DataAdapter As _ New Microsoft.Data.Odbc.OdbcDataAdapter(Command) Dim CommantBuilder As _ New Microsoft.Data.Odbc.OdbcCommandBuilder(DataAdapter) Dim DataSet As New System.Data.DataSet()
Haben wir die Verbindung aufgebaut, müssen wir sie nur noch öffnen und unser
Connection.Open()
DataAdapter.Fill(DataSet, "DOCS")
Anschließend deklarieren wir eine DataRow und weisen Ihr eine neue Reihe aus unserem DataSet zu:
Dim DataRow As System.Data.DataRow = DataSet.Tables("DOCS").NewRow
Dieses neue DataRow können wir nun mit unseren Daten füllen:
DataRow.Item("contenttype") = ContentType
DataRow.Item("contentlength") = ContentLength
DataRow.Item("filename") = FileName
DataRow.Item("filecontent") = FileContent
Dann fügen wir den neuen Datensatz in Unser DataSet ein und Aktualisieren hiernach die Datenbank:
DataSet.Tables("DOCS").Rows.Add(DataRow)
DataAdapter.Update(DataSet, "DOCS")
Jetzt sollten wir alle Objekte zerstören oder leeren, die wir innerhalb der If-Abfrage benutzt haben und diese beenden:
DataRow = Nothing DataSet.Dispose() DataSet = Nothing CommantBuilder.Dispose() CommantBuilder = Nothing DataAdapter.Dispose() DataAdapter = Nothing Command.Dispose() Command = Nothing Connection.Close() Connection.Dispose() Connection = Nothing End If
Anschließend zerstören wir das Byte-Array, den Stream und beenden die For-Schleife:
FileContent = Nothing IOStream = Nothing Next
Nun folgt nur noch die Zerstörung der FileColletion, die Ausgabe einer Statusmeldung in das Label und fertig ist die Upload-Funktion:
FileCollection = Nothing Dim strMsg As String = Counter & _ " Dateien wurden in die Datenbank geladen" Label1.Text = strMsg End Sub
Um die Funktion aufzurufen müssen wir noch folgenden Code in die Funktion "Page_Load" einfügen:
If Page.IsPostBack Then saveInDB() End If
Diese Zeilen sorgen dafür, dass die Funktion nur ausgeführt wird, wenn das Formular gesendet wurde.
Abschließende Tipps
Um die Dateigröße zu begrenzen, z. B. auf 4000 Bytes, kann man folgende paar Zeilen vor die If-Abfrage (If status > 0 then ...) einfügen:
Dim errMsg as String If status > 4000 then status = 0 errMsg &= "Die Datei ""<b>" errMsg &= FileCollection(i).FileName.toString errMsg &= "</b>"" ist zu groß!<br />" End If
Da hier "status" 0 zugewiesen wird, werden die Anweisungen innerhalb der nächsten If-Abfrage nicht mehr ausgeführt. Um die Meldung auszugeben, können Sie diese einfach an die Statusmeldung am Ende der Sub anfügen:
Dim strMsg As String = Counter & _ " Dateien wurden in die Datenbank geladen" Label1.Text = strMsg &"<br />&" errMsg
Das selbe können Sie machen, wenn Sie nur ein bestimmtes Dokumentenformat hochladen wollen:
Dim errMsg as String If FileCollection(i).ContentType.toString = ";application/pdf” then status = 0 errMsg &= "Die Datei ""<b>" errMsg &= FileCollection(i).FileName.toString errMsg &= "</b>"" hat das falsche Format! <br />" End If
Donwload: asp.net.fileupload.zip (158 KB)