Zippen mit Access

Wenn Sie ein Abonnement des Magazins 'Access im Unternehmen' besitzen, können Sie sich anmelden und den kompletten Artikel lesen.
Anderenfalls können Sie das Abonnement hier im Shop erwerben.

Zippen mit Access

Sie werden immer mal wieder auf die Aufgabe stoßen, automatisiert Zip-Dateien zu erstellen, Daten in Zip-Dateien zu speichern oder Daten aus Zip-Dateien zu extrahieren. Dazu benötigen Sie optimalerweise VBA-Code ohne viel Schnickschnack wie externe Bibliotheken et cetera. Windows liefert glücklicherweise in aktuelleren Versionen Möglichkeiten, Zip-Dateien auch per VBA zu erstellen, zu füllen und zu lesen. Diese sind zwar nicht so einfach zu finden, aber wir haben Ihnen eine Auswahl wichtiger Funktionen zusammengestellt.

Grundlegende Technik

Wenn wir die Windows-Funktionen zum Verwenden von Zip-Dateien nutzen wollen, haben wir nur eine eingeschränkte Menge an Möglichkeiten zur Verfügung. Diese sollten jedoch für den Großteil der denkbaren Einsatzfälle ausreichend sein.

Benötigen Sie spezielle Techniken etwa zum Hinzufügen eines Kennworts zum Schutz des Inhalts der Zip-Datei, müssen Sie auf fertige Bibliotheken zurückgreifen, die möglicherweise kostenpflichtig sind und gegebenenfalls auch erst beim Nutzer installiert werden müssen.

Die hier vorgestellten Techniken nutzen allein die vom Betriebssystem bereitgestellten Funktionen.

In einem halbwegs frischen System, also ohne zusätzliche Einträge in den Kontextmenüs, sieht die eingebaute Funktion zum Erstellen von Zip-Dateien wie in Bild 1 aus.

Erstellen einer Zip-Datei mit Windows-Bordmitteln

Bild 1: Erstellen einer Zip-Datei mit Windows-Bordmitteln

Zip-Datei erstellen

Es gibt keine eingebaute Funktion, um eine leere Zip-Datei zu erstellen. Wie können wir dieses Problem umgehen? Dazu gibt es zumindest zwei Möglichkeiten:

  • Wir legen eine neue, leere Zip-Datei mit einem geeigneten Programm wie WinZip oder WinRar an, speichern diese in einem Anlage-Feld der Datenbank und exportieren dieses bei Bedarf als leere Zip-Datei in das Zielverzeichnis.
  • Wir schauen uns an, wie eine leere Zip-Datei aufgebaut ist, und bauen diese einfach nach.

Ersteres würde zumindest noch eine weitere Tabelle mit dem Anlage-Feld erforderlich machen, was wir an dieser Stelle für übertriebenen Aufwand halten. Also erstellen wir lieber mit WinZip oder WinRar eine leere Zip-Datei und betrachten diese in einem Texteditor.

Das Ergebnis sieht dann etwa wie in Bild 2 aus. Was soll schon schieflaufen, wenn wir einfach eine leere Textdatei mit den eingebauten VBA-Befehlen erzeugen und die hier enthaltenen Zeichen einfügen? Also machen wir uns an die Arbeit. Die Prozedur aus Listing 1 erwartet den Namen der zu erstellenden Zip-Datei und legt diese dann an. Dazu erstellt sie eine String-Variable namens strZipinhalt und füllt sie genau mit dem Inhalt der Datei, die wir oben probehalber erzeugt und im Texteditor angezeigt haben. Die Zeichen in der verwendeten Vorlage werden vom Texteditor im hexadezimalen Format angezeigt. Wir müssen diese, bevor wir die Zeichen mit der Funktion Chr() ermitteln, in das Dezimalformat umwandeln. Dies erledigen wir durch Voranstellen der Zeichenkette &H. Die achtzehn Nullen fügen wir mit der Funktion String() hinzu, der wir als ersten Parameter die Anzahl der zu liefernden Zeichen und als zweiten den Code für das gewünschte Zeichen übergeben.

Aussehen einer leeren Zip-Datei

Bild 2: Aussehen einer leeren Zip-Datei

Die Prozedur ermittelt dann mit der Funktion FreeFile eine Dateinummer für den Zugriff auf die neu zu erstellende Datei. Diese öffnen wir mit der Open-Methode, der wir den Namen der zu erstellenden Datei folgen lassen (strZipdatei). Der Öffnungsmodus ist For Binary Access Write, die Dateinummer lautet schließlich #intDateinummer. Die Put-Methode schreibt den Inhalt der Variablen strZipinhalt in die mit #intDateinummer geöffnete Datei, die Close-Methode schließt diese wieder.

Ein beispielhafter Aufruf für diese Prozedur sieht etwa wie folgt aus:

Public Sub Test_ZipErstellen()
     On Error Resume Next
     Kill CurrentProject.Path & " estvba.zip"
     On Error GoTo 0
     ZipErstellen CurrentProject.Path & "	estvba.zip"
End Sub

Die so erzeugte Datei hat genau den gleichen Inhalt wie die oben mit WinZip erzeugte Datei im Texteditor und kann somit auch etwa mit WinZip oder WinRar geöffnet werden. Außerdem, und das ist ja unser erklärtes Ziel, können wir diese neu erstellte Zip-Datei nun mit den von uns gewünschten Dateien füllen.

Zip-Datei füllen

Der zweite Schritt besteht nun darin, eine Datei zu einer Zip-Datei hinzuzufügen. Dazu verwenden wir die Funktion Zippen aus Listing 2. Die Datei erwartet zwei Parameter:

Public Function Zippen(strZipdatei As String, strDatei As String) As Boolean
     Dim objZip As Object
     Dim objShell As Object
     Dim intCount As Integer
     Set objShell = CreateObject("Shell.Application")
     Set objZip = objShell.Namespace((strZipdatei))
     If objZip Is Nothing Then
         ZipErstellen strZipdatei
         Set objZip = objShell.Namespace(CVar(strZipdatei))
     End If
     intCount = objZip.items.Count
     'objZip.CopyHere strDatei
     objZip.CopyHere (strDatei) 'Klammer Pflicht, da sonst kein zippen
     Do
         Call Sleep(100)
     Loop While Not ZipdateiGeschlossen(strZipdatei)
     If objZip.items.Count > intCount Then
         Zippen = True
     End If
End Function

Listing 2: Funktion zum Hinzufügen einer Datei zu einer Zip-Datei

  • strZipdatei ist der Pfad zur Zip-Datei,
  • strDatei ist der Pfad zu der hinzuzufügenden Datei.

Die Funktion deklariert zwei Objektvariablen namens objZip und objShell sowie eine Integer-Variable namens intCount. objShell füllen wir mit einem neuen Objekt auf Basis der Klasse Shell.Application wie bereits in der Prozedur ZipErstellen. objZip erhält einen Verweis auf das Namespace-Objekt auf Basis der Zip-Datei.

Diesen holen wir über die Namespace-Eigenschaft des Shell.Application-Objekts. Danach prüft die Funktion, ob objZip einen Verweis enthält. Dies ist nicht der Fall, wenn die mit strZipdatei übergebene Datei keine Zip-Datei ist. In diesem Fall erstellt die Funktion die Zip-Datei mit der bereits vorgestellten Prozedur ZipErstellen neu und erneuert dann den in objZip gespeicherten Verweis auf diese Datei.

Nun folgt ein Schritt, der für die Überprüfung des Erfolgs wichtig ist. Mit der Eigenschaft Count der items-Auflistung des Namespace-Objekts mit der Zip-Datei ermitteln wir die Anzahl der in der Zip-Datei enthaltenen Dateien beziehungsweise Elemente (hier werden auch Ordner mitgezählt). Bei einer frisch angelegten Zip-Datei sollte dies den Wert 0 liefern.

Anschließend würden wir normalerweise einfach die CopyHere-Methode des Namespace-Objekts mit der Zip-Datei aufrufen und als Parameter den Namen der hinzuzufügenden Datei übergeben:

objZip.CopyHere strDatei

Dies ist auf dem Testsystem jedoch zuverlässig schiefgegangen. Nach einigen Recherchen stellte sich heraus, dass man den Dateinamen in Klammern einfassen muss, damit es gelingt:

objZip.CopyHere (strDatei)

Die Methode CopyHere ist eine asynchrone Methode, das heißt, sie wird ausgeführt, während der Code weiterläuft. Wenn wir also direkt danach mit der folgenden Anweisung prüfen würden, ob die Datei hinzugefügt wurde, kann es sein, dass das Hinzufügen noch nicht abgeschlossen wurde:

If objZip.items.Count > intCount Then

Also bauen wir eine Do...Loop-Schleife ein, die mit einer Hilfsfunktion prüft, ob die Datei bereits zur Zip-Datei hinzugefügt wurde. Mit jedem Durchlauf dieser Schleife rufen wir zunächst die Sleep-Funktion mit dem Wert 100 als Parameter auf, was eine Pause von einer Zehntelsekunde hervorruft. Die Sleep-Funktion deklarieren wir wie folgt in einem Standardmodul:

Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)

Die Abbruchbedingung lautet so:

Loop While Not ZipdateiGeschlossen(strZipdatei)

Die hier angegebene Funktion ZipdateiGeschlossen erwartet den Namen der zu überprüfenden Datei als Parameter (s. Listing 3). Sie ermittelt eine Nummer für den schreibenden Zugriff auf die angegebene Datei und versucht dann, diese mit der Open-Anweisung zu öffnen.

Private Function ZipdateiGeschlossen(strZipdatei As String) As Boolean
     Dim intDateinummer As Integer
     intDateinummer = FreeFile
     On Error GoTo Ende_ZipdateiGeschlossen
     Open strZipdatei For Binary Access Read Lock Write As intDateinummer
     Close intDateinummer
     ZipdateiGeschlossen = True
Ende_ZipdateiGeschlossen:
End Function

Listing 3: Funktion zur Prüfung, ob eine bestimmte Datei aktuell geschlossen ist

Ist die Datei noch geöffnet, löst diese Anweisung einen Fehler aus und die Funktion springt zur Marke Ende_ZipdateiGeschlossen. Die Funktion liefert dann den Wert False zurück. Anderenfalls schließt die Funktion die Datei mit der Close-Anweisung wieder und stellt den Rückgabewert auf True ein.

Auf diese Weise wird die Do...Loop-Schleife so lange durchlaufen, bis die Zip-Datei nicht mehr durch die CopyHere-Anweisung in Beschlag genommen wird und die Datei hinzugefügt wurde. Danach können wir dann in der If...Then-Bedingung prüfen, ob sich die Anzahl der enthaltenen Dateien im Anschluss an das Hinzufügen geändert hat. Falls ja, wird der Rückgabewert der Funktion Zippen auf den Wert True eingestellt.

Die Funktion Zippen testen wir etwa mit folgender Routine:

Public Sub Test_Zippen_Datei_klein()
     Dim strDatei As String
     Dim strZipdatei As String
     strZipdatei = CurrentProject.Path & " estvba.zip"
     strDatei = CurrentProject.Path & "	est.txt"
     MsgBox "Zippen erfolgreich? " _
         & Zippen(strZipdatei, strDatei)
End Sub

Dies stellt die Pfadangaben zur Zip-Datei und zur hinzuzufügenden Datei zusammen und übergibt diese an die Funktion Zippen. Das Ergebnis wird dann in einer Meldung ausgegeben.

Verzeichnisse zippen

Mit der Funktion Zippen können Sie auch komplette Verzeichnisse zippen. Dazu übergeben Sie als zweiten Parameter einfach den Pfad zu dem zu zippenden Verzeichnis:

Public Sub Test_Zippen_Folder()
     Dim strDatei As String
     Dim strZipdatei As String
     strZipdatei = CurrentProject.Path & "	estvba.zip"
     strDatei = CurrentProject.Path & "	est"
     MsgBox "Zippen erfolgreich? " _
         & Zippen(strZipdatei, strDatei)
End Sub

Nachdem Sie diese beiden Test-Routinen aufgerufen haben, erhalten Sie eine Zip-Datei, die mit WinRar geöffnet etwa wie in Bild 3 aussieht.

Zip-Datei mit einigen per VBA hinzugefügten Elementen

Bild 3: Zip-Datei mit einigen per VBA hinzugefügten Elementen

Die bisher beschriebene Version der Funktion Zippen haben wir unter dem Namen Zippen_Version1 im Modul mdlZippen abgelegt.

Dateien eines Verzeichnisses einzeln zippen

Nun möchten Sie möglicherweise nicht das komplette Verzeichnis zippen, sondern nur die in diesem Verzeichnis enthaltenen Dateien. Das können Sie erledigen, indem Sie die Funktion Zippen entsprechend anpassen.

Die neue Version sieht nun wie in Listing 4 aus. Sie erwartet zwei Parameter mehr als die vorherige Version, nämlich lngAnzahl und bolOhneVerzeichnis. Beide sind nur in Zusammenhang mit der Übergabe eines Verzeichnisses mit dem Parameter strDateiVerzeichnis interessant, da nur dann mehrere Dateien verarbeitet werden.

Public Function Zippen(strZipdatei As String, ByVal strDateiVerzeichnis As String, _
         Optional lngAnzahl As Long, Optional bolOhneVerzeichnis As Boolean = False) As Boolean
     Dim objZip As Object, objShell As Object
     Dim intCount As Integer, strDatei As String, strVerzeichnis As String
     Set objShell = CreateObject("Shell.Application")
     Set objZip = objShell.NameSpace((strZipdatei))
     If objZip Is Nothing Then
         ZipErstellen strZipdatei
         Set objZip = objShell.NameSpace(CVar(strZipdatei))
     End If
     intCount = objZip.items.Count
     If IstVerzeichnis(strDateiVerzeichnis) And bolOhneVerzeichnis Then
         strVerzeichnis = Trim(strDateiVerzeichnis)
         If Not Right(strVerzeichnis, 1) = "" Then
             strVerzeichnis = strVerzeichnis & ""
         End If
         strDatei = Dir(strVerzeichnis)
         Do While Len(strDatei) > 0
             objZip.CopyHere (strVerzeichnis & strDatei)
             Do
                 Call Sleep(100)
             Loop While Not ZipdateiGeschlossen(strZipdatei)
             strDatei = Dir()
         Loop
     Else
         objZip.CopyHere (strDateiVerzeichnis)
         Do
             Call Sleep(100)
         Loop While Not ZipdateiGeschlossen(strZipdatei)

Dies war die Leseprobe dieses Artikels.
Melden Sie sich an, um auf den vollständigen Artikel zuzugreifen.

Bitte geben Sie die Zeichenfolge in das nachfolgende Textfeld ein

Die mit einem * markierten Felder sind Pflichtfelder.

Ich habe die Datenschutzbestimmungen zur Kenntnis genommen.