Ich bin vor einiger Zeit über das Hindernis gestoßen, dass man im .NET Framework nur Dateien mit System.IO.File.Copy() als ein Ganzes kopieren kann. Möchte man nun z.B. eine große Datei kopieren, hat man keine Möglichkeit den Fortschritt des Kopiervorgangs auf der GUI, etwa durch eine Prozessbar, sichtbar zu machen.
Dieses Manko wollte ich beheben und hab mich dazu entschlossen eine Klasse zu schreiben, die Dateien binär kopiert und während dieses Vorgangs einen Fortschritt berechnet. Um das Ganze zu komplettieren habe ich auch gleich die Berechnung des Datendurchsatzes und der Restzeit des Kopiervorgangs mit eingebaut.
Um die Funktionalität nicht nur auf eine einzelne zu kopierende Datei zu beschränken, bietet die Klasse auch die Möglichkeit eine Liste von Dateien zu übergeben in Form einer System.Collections.Generic.List<string>, eines string[] Arrays oder eines System.IO.FileInfo[] Arrays. Die Klasse informiert dann während des Kopierens über den prozentualen Fortschritt der Dateiliste, der aktuell zu kopierenden Datei und bezogen auf die Datei die grad kopiert wird ebenfalls Fortschritt in Prozent, Datendurchsatz und Restzeit. Auf diese Weise ließe sich eine Kopier-Fortschritts-Ansicht erstellen, wie man es z.B. aus dem TotalCommander kennt. Eine Prozessbar für die gesamten zu kopierenden Dateien und eine für den Kopiervorgang der aktuellen Datei.
Die Klasse gibt alle Informationen über den Kopiervorgang per Events nach draußen, welche dann abonniert werden können. Der Komponente liegt eine Beispiel Applikation bei, die die CopyComponent in einem BackgroundWorker verwendet. Die wesentlichen Codestücke zur Verwendung habe ich noch mal hervorgehoben:
// Instanz der BinaryCopiers
BinaryCopy bCopy = new BinaryCopy();
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
// Event des BinaryCopiers registrieren
bCopy.BinaryCopyEvent += new BinaryCopyEventHandler(bCopy_BinaryCopyEvent);
// Kopie der Datei starten
bCopy.Copy(tbSource.Text, tbDestination.Text, true);
}
void bCopy_BinaryCopyEvent(object sender, BinaryCopyEventArgs e)
{
// Auf das BinaryCopy Event reagieren und beim BGW das ProgressChanged Event auslösen und die BinaryCopyEventArgs übergeben
backgroundWorker.ReportProgress(e.Percent, (object)e);
}
private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// EventArgs abfragen
BinaryCopyEventArgs binaryArgs = (e.UserState as BinaryCopyEventArgs);
// Prozessbar aktualisiern
tsProgressBar.Value = e.ProgressPercentage;
// Datendurchsatz in einem Label anzeigen
tsslRate.Text = binaryArgs.KbPSec.ToString("N").Substring(0, binaryArgs.KbPSec.ToString("N").Length - 3) + " Kb/sec";
// Restzeit in einem Label anzeigen
tsslRemaning.Text = binaryArgs.Remaning.ToString();
}
Die Klasse mit der Beispielanwendung befindet sich im Anhang. Für Korrekturen (auch am Stil) und Verbesserungsvorschläge bin ich natürlich offen.
UPDATE vom 11.10.2010 (Datei-Downloads vor Update: 229):
- Verbesserungsvorschläge von Abt übernommen (siehe Postings weiter unten)
- Eine Methode zum kopieren ganzer Ordner samt Inhalt hinzugefügt
UPDATE vom 26.03.2014 (Datei-Downloads vor Update: 459):
- Verbessertes kopieren von Ordnern, welches nun auch per Event über den Fortschritt des kopierens eines ganzen Ordners informiert.
- Sample App auf die Veränderung angepasst
if (newFileName != string.Empty)
{
if (File.Exists(destination + @"\" + newFileName) && !overwrite)
throw new IOException("Die im Zielverzeichnis vorhandene Zieldatei kann nicht überschrieben werden");
}
else
{
if (File.Exists(destination + @"\" + srcFileInfo.Name) && !overwrite)
throw new IOException("Die im Zielverzeichnis vorhandene Zieldatei kann nicht überschrieben werden");
}
habe ich bei mir in
if ( newFileName != string.Empty )
{
if ( File.Exists( Path.Combine( destination, newFileName ) ) && !overwrite )
throw new IOException( "Die im Zielverzeichnis vorhandene Zieldatei kann nicht überschrieben werden" );
}
else
{
if ( File.Exists( Path.Combine( destination, srcFileInfo.Name ) )&& !overwrite )
throw new IOException( "Die im Zielverzeichnis vorhandene Zieldatei kann nicht überschrieben werden" );
}
ändern müssen, da ich bei mir keine fixen Pfadkomponenten im Quelltext stehen haben darf/möchte und stattdessen auf Path.Combine() setze.
Bei der Berechnung der kbPSec könnte es zu einem Umrechnungsfehler kommen, wenn bytesReceived und sec beides Ganzzahlen sind, kbPSec aber ein Double darstellt - erklärt jedenfalls die Anzeige bei mir die mitten drin plötzlich 0.0 anzeigt.
Weiterhin hab ich bei mir den Event BinaryCopyEventArgs um das Argument bytesReceived erweitert, sodass ich bei einer Vielzahl von Copy-Prozessen den Gesamtfortschritt haargenau anzeigen kann.
vielen Dank für deine Anmerkungen und Verbesserungsvorschläge. Ich habe die Snipp überarbeitet und im ersten Post aktualisiert. Zusätzlich bietet die Komponente nun noch eine Methode zum kopieren ganzer Ordner samt Unterordner und Dateien.
das Tool ist nicht schlecht, jedoch werden immer alle Dateien kopiert die in der Quelle vorhanden sind. gibt es eine möglichkeit vorher zu vergleichen was im Ziel vorhanden ist bzw. älter ist, da nur neue und fehlende Dateien kopiert werden sollen?
...und mittlerweile hab ich QuickIO.NET - Performante Dateioperationen veröffentlich, mit dem so etwas sehr einfach realisierbar ist.
Was aber genau "gleich" bei Dateien bedeutet verstehen viele anders:
Einfach zwei Listen mit den Dateien aus Quelle und Ziel erstellen.
Beide nach eigenen Kriterien vergleichen.
Aktualisierte / Neue Dateien kopiere.
-> Fertig.
Diese gewünschte Funktionalität wird in der Tat nicht unterstützt. Da aber die Sourcen vorhanden sind, kann man sich das leicht nach-implementieren. Oder wie von den Vorrednern erwähnt sich die Listen vorbereiten und dann erst zum kopieren schicken.
Der Fokus dieser Snippet liegt auf der einfachen Einbindung ins eigene Projekt und der Lösung eines konkreten Problems. Wer ein mächtigeres Kopier-Werkzeug mit deutlich mehr Funktionalität haben will, der sollte sich bei Abt's QuickIO bedienen.
@digicorder Solltest du dir die Komponente erweitern, dann kannst dir mir deine Änderungen gerne schicken und ich aktualisiere den Thread damit.