Rubrik: Dateisystem · Dateien lesen/schreiben | VB-Versionen: VB2005, VB2008 | 19.03.10 |
Manchmal ist es nicht möglich, eine Datei aufgrund ihrer Größe als ganzes zu übertragen. Sei es, weil der Emailprovider die Anhanggröße einer Mail beschränkt oder ein Speichermedium nicht über genügend Platz verfügt.
Szenarien gibt es viele.
Als Lösung bietet sich ein Splitten der Datei in einzelne Segmenten an. Die einzelnen Dateisegmente können dann bei Bedarf wieder zu einer Datei zusammengefügt werden. Genau dieses demonstriert dieses Beispiel anhand
einer einfachen Klasse. Ich habe zugunsten der Lesbarkeit dieses Beispiels auf diverse Absicherungen verzichtet, die man später aber implementieren sollte (die Segmentgröße darf als Beispiel nicht größer als der Bereich
eines Int32 sein usw.).
Imports System.IO
''' <summary>
''' Größeneinheit des einzelnen Segmentes
''' </summary>
''' <remarks></remarks>
Public Enum SizeUnit
KByte
MByte
GByte
End Enum
Public Class SplittingJoining
''' <summary>
''' Datei in Segmente splitten
''' </summary>
''' <param name="SourceFile">Quelldatei</param>
''' <param name="TargetFolder">Zeilverzeichnis</param>
''' <param name="Unit">Dateneinheitgröße</param>
''' <param name="Size">Größe der Dateneinheit</param>
''' <remarks></remarks>
Public Sub SplitFile(ByVal SourceFile As String, _
ByVal TargetFolder As String, _
ByVal Unit As SizeUnit, _
ByVal Size As Integer)
' Dient zur Überprüfung, ob das letzte Segment eingelesen wird
Dim controlBuffer As Int32 = 0
' Definiert die Anzahl an Bytes, die Stückweise einzulesen und zu schreiben sind
Dim bytesToRead As Int32 = 0
' Tatsächliche eingelesene Bytes
Dim bytesRead As Int32 = 0
' Wird den Namen des jeden neu zu schreibenden Dateisegmentes beinhalten
Dim currDstFile As String
' Zähler für die Dateiendungen
Dim counter As Int32 = 1
' Dient zum einlesen der Quelldatei
Dim srcStream As FileStream
' Abhängig von der Benutzerangabe bezüglich der Dateiteinheitengröße
' den Lesebuffer festlegen
If Unit = SizeUnit.KByte Then
bytesToRead = Size * 1024
ElseIf Unit = SizeUnit.MByte Then
bytesToRead = Size * (1024 * 1024)
ElseIf Unit = SizeUnit.GByte Then
bytesToRead = Size * (1024 * 1024 * 1024)
End If
controlBuffer = bytesToRead
' Filestream aus der Quelldatei erstellen
srcStream = New FileStream(SourceFile, FileMode.Open, _
FileAccess.Read, FileShare.None)
Try
Dim buffer(bytesToRead - 1) As Byte
Do
' Dateiendung ermitteln.
' Diese wird gegebenfalls mit Nullen aufgefüllt.
If counter < 10 Then
' Liegt die Zahl unter 10, so wird der Dateieindung zwei
' Nullen vorangestellt (.001, 002 usw.)
currDstFile = "00" & counter.ToString()
ElseIf counter < 100 Then
' Liegt die Zahl unter 100, so wird der Dateieindung eine
' Null vorangestellt (.020, 021 usw.)
currDstFile = "0" & counter.ToString()
Else
currDstFile = counter.ToString()
End If
' Name des neu zu schreibenden Dateisegmentes
currDstFile = TargetFolder + "\" + _
Path.GetFileName(SourceFile) + "." + currDstFile
' Letztes Segment erstellen?
If ((srcStream.Length - srcStream.Position) < controlBuffer) _
OrElse ((srcStream.Length - srcStream.Position) = controlBuffer) Then
' Einzulesende Restgröße ermitteln
bytesToRead = CInt((srcStream.Length - srcStream.Position))
If bytesToRead = 0 Then
' Es gibt nichts mehr einzulesen
Exit Do
End If
End If
' Daten einlesen
bytesRead = srcStream.Read(buffer, 0, bytesToRead)
' Dateisegment schreiben
Using dstStream As New FileStream(currDstFile, _
FileMode.Create, FileAccess.ReadWrite, FileShare.None)
Try
dstStream.Write(buffer, 0, bytesToRead)
Catch ex As Exception
MessageBox.Show("Beim schreiben der Daten trat folgender Fehler auf:" + _
vbCrLf + ex.Message, _
"Hinweis", MessageBoxButtons.OK, MessageBoxIcon.Error)
Exit Sub
End Try
End Using
' Zähler erhöhen
counter += 1
Loop While bytesRead = controlBuffer AndAlso bytesToRead > 0
MessageBox.Show("Die Datei wurde erfolgreich gesplittet", _
"Hinweis", MessageBoxButtons.OK, MessageBoxIcon.Information)
Catch ex As Exception
MessageBox.Show("Beim lesen / schreiben der Daten trat folgender Fehler auf:" + _
vbCrLf + ex.Message, _
"Hinweis", MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
End Sub
''' <summary>
''' Setzt eine Datei aus Dateifragmenten wieder zusammen.
''' </summary>
''' <param name="f">Erstes gesplittetes Dateisegment (Extension 001!!)</param>
''' <param name="TargetFolder">Zielverzeichnis</param>
Public Sub JoinFileSegments(ByVal f As String, _
ByVal TargetFolder As String)
' Zähler für die Dateiendungen
Dim counter As Integer = 1
' Bytes, die einzulesen sind
Dim byteToRead As Integer = 0
' Beinhaltet die eingelesenen Daten
Dim byteRead As Integer = 0
' Liste wird die Dateisegmente enthalten
Dim fileList As New List(Of String)()
Dim b As String = ""
' Alle Dateisegmente in einer List(Of String)speichern
Do
If counter < 10 Then
b = "00" & counter.ToString
ElseIf counter < 100 Then
b = "0" & counter.ToString
Else
b = counter.ToString
End If
Dim filename As String = Path.GetDirectoryName(f) + "\" + _
Path.GetFileNameWithoutExtension(f) + "." + b
If File.Exists(filename) Then
fileList.Add(filename) ' Dateisegment in die Liste aufnehmen
End If
counter += 1
Loop While File.Exists(Path.GetDirectoryName(f) + "\" + _
Path.GetFileNameWithoutExtension(f) + "." + b)
Try
' Ziel-FileStream erstellen, durch welchem die Dateisegmente
' in eine Zieldatei geschrieben werden
Dim dstStream As New FileStream(TargetFolder + "\" + _
Path.GetFileNameWithoutExtension(f), _
FileMode.Create, FileAccess.Write, FileShare.None)
' Enhält die einzulesenden Daten
Dim filedata As Byte()
' Alle Dateien aus der Liste auslesen und in eine Zieldatei speichern
For Each fi As String In fileList
' FileStream, der das Datei-Segment einliest.
Using sceStream As New FileStream(fi, FileMode.Open, _
FileAccess.Read, FileShare.Read)
' Länge der einzulesenden Daten ermitteln
byteToRead = CInt(sceStream.Length)
filedata = New Byte(byteToRead - 1) {}
' Daten einlesen
byteRead = sceStream.Read(filedata, 0, byteToRead)
' Daten schreiben
dstStream.Write(filedata, 0, byteRead)
End Using
Next
dstStream.Close()
Catch ex As Exception
MessageBox.Show(ex.Message)
Exit Sub
End Try
MessageBox.Show("Datei-Joining wurde erfolgreich beendet")
End Sub
End Class