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 DataSet füllen:

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)