Hi
habe soeben eine Klasse geschrieben, die das asynchrone Kopieren von Dateien und Ordnern ermöglicht:
Um weitere Informationen, wie z.B. das Startdatum des Kopiervorgangs weiterzugeben, kann man eine Klasse von CopyProgress erben lassen und diese Information manuell setzen. Dazu sollte man dann eben FileCopy.CreateProgress überschreiben. Für allgemeine Kopiervorgänge, für die das nicht benötigt wird, genügt eine FileCopy-Instanz; wenn man die Standardpuffergröße von 4096 Bytes verwenden will, sollte man einfach auf die Instanz
Für frühere Frameworks kann statt des
Es handelt sich leider nur um einen Auszug aus der Datei, da sonst die max. Anzahl an Zeichen überschritten wird. Die vollständige Datei befindet sich im Anhang. Ein Beispiel folgt im nächsten Posting.
Gruß
~blaze~
habe soeben eine Klasse geschrieben, die das asynchrone Kopieren von Dateien und Ordnern ermöglicht:
VB.NET-Quellcode
- ''' <summary>
- ''' Ermöglicht das asynchrone Kopieren mit Fortschrittsüberwachung von Dateien, Verzeichnissen und Datenströmen.
- ''' </summary>
- Public Class FileCopy
- ''' <summary>
- ''' Stellt eine Instanz bereit, die für Standardkopiervorgänge von Dateien verwendet werden kann.
- ''' </summary>
- Public Shared ReadOnly General As FileCopy = New FileCopy()
- Private ReadOnly _bufferSize As Integer
- ''' <summary>
- ''' Initialisiert eine neue Instanz der FileCopy-Klasse unter Verwendung der Standardpuffergröße von 4096.
- ''' </summary>
- Protected Sub New()
- Me.New(4096)
- End Sub
- ''' <summary>
- ''' Initialisiert eine neue Instanz der FileCopy-Klasse unter Verwendung der angegebenen Puffergröße.
- ''' </summary>
- ''' <param name="bufferSize">Die Größe der Puffer, die zum Kopieren verwendet werden soll.</param>
- Public Sub New(ByVal bufferSize As Integer)
- _bufferSize = bufferSize
- End Sub
- ''' <summary>
- ''' Kopiert die angegebene Datei an die Zieldatei.
- ''' </summary>
- ''' <param name="sourceFile">Der Pfad zur Quelldatei.</param>
- ''' <param name="destinationFile">Der Pfad mit Dateinamen zur Zieldatei.</param>
- ''' <returns>Ein Objekt, das zum Überwachen des Fortschritts verwendet werden kann.</returns>
- Public Function CopyFile(ByVal sourceFile As String, ByVal destinationFile As String) As CopyProgress
- Dim sf As IO.Stream = IO.File.OpenRead(sourceFile), df = IO.File.OpenWrite(destinationFile)
- Dim progress As CopyProgress = CreateProgress(sf.Length, 1)
- AddHandler progress.Finished, Sub(sender, e)
- sf.Close()
- df.Close()
- End Sub
- CopyAsync(progress, New KeyValuePair(Of IO.Stream, IO.Stream)(sf, df))
- Return progress
- End Function
- ''' <summary>
- ''' Kopiert das angegebene Verzeichnis in das Zielverzeichnis.
- ''' </summary>
- ''' <param name="sourceDirectory">Das Quellverzeichnis.</param>
- ''' <param name="destinationDirectory">Das Zielverzeichnis.</param>
- ''' <param name="searchOption">Gibt an, ob nur das angegebene Verzeichnis oder alle Unterverzeichnisse kopiert werden sollen.</param>
- ''' <returns>Ein Objekt, das zum Überwachen des Fortschritts verwendet werden kann.</returns>
- Public Function CopyDirectory(ByVal sourceDirectory As String, ByVal destinationDirectory As String, ByVal searchPattern As String, ByVal searchOption As IO.SearchOption) As CopyProgress
- Dim files() As String = System.IO.Directory.GetFiles(sourceDirectory, searchPattern, searchOption)
- Dim pl(files.Length - 1) As KeyValuePair(Of System.IO.Stream, System.IO.Stream)
- Dim len As Long = 0
- Dim progress As CopyProgress
- For i As Integer = 0 To files.Length - 1
- Dim target As String = IO.Path.Combine(destinationDirectory, files(i).Substring(sourceDirectory.Length + 1))
- IO.Directory.CreateDirectory(IO.Path.GetDirectoryName(target))
- Dim sf As IO.Stream = IO.File.OpenRead(files(i)), df = System.IO.File.OpenWrite(target)
- len += sf.Length
- pl(i) = New KeyValuePair(Of System.IO.Stream, System.IO.Stream)(sf, df)
- Next
- progress = CreateProgress(len, files.Length)
- AddHandler progress.Finished, Sub()
- For Each cp As KeyValuePair(Of System.IO.Stream, System.IO.Stream) In pl
- cp.Key.Close()
- cp.Value.Close()
- Next
- End Sub
- CopyAsync(pl, progress)
- Return progress
- End Function
- ''' <summary>
- ''' Kopiert die Daten des <see cref="System.IO.Stream"/> an das Ziel.
- ''' </summary>
- ''' <param name="source">Die Datenquelle.</param>
- ''' <param name="destination">Das Datenziel.</param>
- ''' <returns>Ein Objekt, das zum Überwachen des Fortschritts verwendet werden kann.</returns>
- Public Function CopyStream(ByVal source As IO.Stream, ByVal destination As IO.Stream) As CopyProgress
- Dim cp As CopyProgress = CreateProgress(-1, source.Length - source.Position, 0)
- CopyAsync(cp, New KeyValuePair(Of IO.Stream, IO.Stream)(source, destination))
- Return cp
- End Function
- ''' <summary>
- ''' Erzeugt eine neue Fortschritts-Überwachung.
- ''' </summary>
- ''' <param name="amount">Die Gesamtzahl der gelesenen Bytes.</param>
- ''' <param name="fileCount">Die Anzahl der gelesenen Dateien.</param>
- ''' <returns>Das Überwachungsobjekt.</returns>
- Protected Function CreateProgress(ByVal amount As Long, ByVal fileCount As Integer) As CopyProgress
- Return CreateProgress(amount, amount, fileCount)
- End Function
- ''' <summary>
- ''' Erzeugt eine neue Fortschritts-Überwachung.
- ''' </summary>
- ''' <param name="amount">Die Gesamtzahl der gelesenen Bytes.</param>
- ''' <param name="estimatedAmount">Die geschätzte Anzahl an Bytes, die gelesen wird.
- ''' Dieser Wert wird verwendet, wenn die Quelle die ermittelten Werte verändern kann.</param>
- ''' <param name="fileCount">Die Anzahl der gelesenen Dateien.</param>
- ''' <returns>Das Überwachungsobjekt.</returns>
- Protected Overridable Function CreateProgress(ByVal amount As Long, ByVal estimatedAmount As Long, ByVal fileCount As Integer) As CopyProgress
- Return New CopyProgress(amount, estimatedAmount)
- End Function
- Private Shared Sub CopyAsync(ByVal progress As CopyProgress, ByVal ParamArray streams() As KeyValuePair(Of IO.Stream, IO.Stream))
- CopyAsync(streams, progress)
- End Sub
- Private Shared Sub CopyAsync(ByVal streams As IEnumerable(Of KeyValuePair(Of IO.Stream, IO.Stream)), ByVal progress As CopyProgress)
- System.Threading.ThreadPool.QueueUserWorkItem(Sub(o)
- Copy(streams, progress, CInt(Math.Min(progress.Amount, 4096)))
- End Sub)
- End Sub
- Private Shared Sub Copy(ByVal streams As IEnumerable(Of KeyValuePair(Of IO.Stream, IO.Stream)), ByVal progress As CopyProgress, ByVal bufferSize As Integer)
- Dim buffer(bufferSize - 1) As Byte
- Dim len As Integer
- Dim canceled As Boolean
- For Each strm As KeyValuePair(Of IO.Stream, IO.Stream) In streams
- Do
- If progress.Canceled Then
- canceled = True
- Exit For
- End If
- len = strm.Key.Read(buffer, 0, buffer.Length)
- If progress.Canceled Then
- canceled = True
- Exit For
- End If
- strm.Value.Write(buffer, 0, len)
- If progress.Canceled Then
- canceled = True
- Exit For
- End If
- progress.AdvanceProgress(len)
- Loop While len <> 0
- progress.StreamCompleted()
- Next
- progress.SetFinished(canceled)
- End Sub
- Private Shared Sub CreateDirectories(ByVal src As String, ByVal dest As String)
- CreateDirectories(New IO.DirectoryInfo(src), System.IO.Directory.CreateDirectory(dest))
- End Sub
- Private Shared Sub CreateDirectories(ByVal src As IO.DirectoryInfo, ByVal dest As IO.DirectoryInfo)
- For Each di As System.IO.DirectoryInfo In src.GetDirectories()
- CreateDirectories(di, dest.CreateSubdirectory(di.Name))
- Next
- End Sub
- End Class
- ''' <summary>
- ''' Stellt eine Möglichkeit zur Überwachung von Kopiervorgängen bereit.
- ''' </summary>
- Public Class CopyProgress
- ''' <summary>
- ''' Tritt ein, sobald ein weiterer Datenblock kopiert wurde.
- ''' </summary>
- ''' <remarks></remarks>
- Public Event ProgressChanged As EventHandler
- ''' <summary>
- ''' Tritt ein, sobald der Kopiervorgang abgeschlossen wird.
- ''' </summary>
- ''' <remarks></remarks>
- Public Event Finished As EventHandler
- ''' <summary>
- ''' Tritt ein, sobald sich der Fortschritt zum ersten mal ändert.
- ''' </summary>
- ''' <remarks></remarks>
- Public Event Started As EventHandler
- Private _amount, _estimatedAmount As Long
- Private _progress As Long
- Private _canceled, _started As Boolean
- Private _cancelSyncObj As System.Threading.ManualResetEventSlim
- ''' <summary>
- ''' Bricht den Kopiervorgang ab.
- ''' </summary>
- ''' <remarks></remarks>
- Public Sub Cancel()
- If Not _canceled Then
- _cancelSyncObj = New System.Threading.ManualResetEventSlim()
- _canceled = True
- End If
- _cancelSyncObj.Wait()
- End Sub
- Friend ReadOnly Property CancelSyncObj As System.Threading.ManualResetEventSlim
- Get
- Return _cancelSyncObj
- End Get
- End Property
- ''' <summary>
- ''' Gibt an, ob der Vorgang abgebrochen wurde.
- ''' </summary>
- Public ReadOnly Property Canceled As Boolean
- Get
- Return _canceled
- End Get
- End Property
- ''' <summary>
- ''' Gibt die Gesamtzahl der geschätzten Bytes an, die gelesen wird oder -1, sofern die Gesamtzahl unbekannt ist.
- ''' </summary>
- Public ReadOnly Property EstimatedAmount As Long
- Get
- Return _amount
- End Get
- End Property
- ''' <summary>
- ''' Gibt die Gesamtzahl der Bytes an, die gelesen wird oder -1, sofern die Gesamtzahl unbekannt ist.
- ''' </summary>
- Public ReadOnly Property Amount As Long
- Get
- Return _amount
- End Get
- End Property
- ''' <summary>
- ''' Gibt die aktuelle Anzahl der gelesenen Bytes an.
- ''' </summary>
- Public ReadOnly Property Progress As Long
- Get
- Return _progress
- End Get
- End Property
- ''' <summary>
- ''' Gibt den Fortschritt in Prozent an. Sofern die Anzahl geschätzt wird, wird die geschätzte Zahl als Referenzwert verwendet.
- ''' </summary>
- Public Function GetPercentage() As Double
- Return If(Amount = 0, 1.0, Progress / GetAmount())
- End Function
- ''' <summary>
- ''' Gibt den Fortschritt relativ zur angegebenen Zahl an. Sofern die Anzahl geschätzt wird, wird die geschätzte Zahl als Referenzwert verwendet.
- ''' </summary>
- Public Function GetPerValue(ByVal value As Integer) As Integer
- Return If(Amount = 0, value, CInt(Progress * value \ GetAmount()))
- End Function
- Private Function GetAmount() As Long
- Return If(Amount = -1, EstimatedAmount, Amount)
- End Function
- Friend Sub AdvanceProgress(ByVal delta As Long)
- SyncLock Me
- Dim progress As Long = progress + delta
- If progress < 0 OrElse progress > Amount Then
- Throw New ArgumentOutOfRangeException("progress")
- End If
- If Not _started Then
- _started = True
- RaiseEvent Started(Me, EventArgs.Empty)
- End If
- _progress = progress
- End SyncLock
- OnProgressChanged(EventArgs.Empty)
- End Sub
- ''' <summary>
- ''' Erhöht den Fortschritt manuell um den angegebenen Wert.
- ''' </summary>
- ''' <param name="delta">Die Zahl der Bytes, um die der Fortschritt erhöht werden soll.</param>
- Protected Sub Advance(ByVal delta As Long)
- AdvanceProgress(delta)
- End Sub
- Friend Sub SetFinished(ByVal canceled As Boolean)
- _amount = Progress
- RaiseEvent Finished(Me, EventArgs.Empty)
- If canceled Then
- CancelSyncObj.Set()
- OnCanceled()
- End If
- End Sub
- Friend Sub StreamCompleted()
- OnStreamCompleted()
- End Sub
- ''' <summary>
- ''' Setzt den Vorgang manuell auf abgeschlossen.
- ''' </summary>
- Protected Sub SetFinished()
- SetFinished(False)
- End Sub
- ''' <summary>
- ''' Initialisiert eine neue Instanz der CopyProgress-Klasse unter Angabe der Gesamtzahl der zu lesenden Bytes.
- ''' </summary>
- ''' <param name="amount">Die Anzahl der zu lesenden Bytes.</param>
- Public Sub New(ByVal amount As Long)
- Me.New(amount, amount)
- End Sub
- ''' <summary>
- ''' Initialisiert eine neue Instanz der CopyProgress-Klasse unter Angabe der Gesamtzahl der zu lesenden Bytes.
- ''' </summary>
- ''' <param name="amount">Die Anzahl der zu lesenden Bytes.</param>
- ''' <param name="estimatedAmount">Die geschätzte Anzahl der zu lesenden Bytes.
- ''' Dieser Wert wird verwendet, sofern die Gesamtzahl der zu lesenden Bytes unbekannt und somit -1 ist.</param>
- Public Sub New(ByVal amount As Long, ByVal estimatedAmount As Long)
- _amount = amount
- _estimatedAmount = estimatedAmount
- End Sub
- ''' <summary>
- ''' Wird aufgerufen, um den Fortschritt an Ereignisabonnenten weiterzugeben.
- ''' </summary>
- ''' <param name="e">Enthält nähere Informationen zum Fortschritt.</param>
- Protected Overridable Sub OnProgressChanged(ByVal e As EventArgs)
- RaiseEvent ProgressChanged(Me, e)
- End Sub
- ''' <summary>
- ''' Wird aufgerufen, sobald ein abgebrochener Vorgang abgeschlossen wurde.
- ''' </summary>
- Protected Overridable Sub OnCanceled()
- End Sub
- ''' <summary>
- ''' Wird aufgerufen, sobald ein einzelner Stream vollständig kopiert wurde.
- ''' </summary>
- Protected Overridable Sub OnStreamCompleted()
- End Sub
- End Class
Um weitere Informationen, wie z.B. das Startdatum des Kopiervorgangs weiterzugeben, kann man eine Klasse von CopyProgress erben lassen und diese Information manuell setzen. Dazu sollte man dann eben FileCopy.CreateProgress überschreiben. Für allgemeine Kopiervorgänge, für die das nicht benötigt wird, genügt eine FileCopy-Instanz; wenn man die Standardpuffergröße von 4096 Bytes verwenden will, sollte man einfach auf die Instanz
FileCopy.General
zugreifen, sonst kann man eine neue Instanz erzeugen mit der Puffergröße erzeugen.Für frühere Frameworks kann statt des
System.Threading.ManualResetEventSlim
s in CopyProgress auch einfach die System.Threading.ManualResetEvent
-Klasse verwendet werden.Es handelt sich leider nur um einen Auszug aus der Datei, da sonst die max. Anzahl an Zeichen überschritten wird. Die vollständige Datei befindet sich im Anhang. Ein Beispiel folgt im nächsten Posting.
Gruß
~blaze~
Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Marcus Gräfe“ ()