Laden...

Ordner Rekursiv auf NAS schreiben mit Progressbar mittels Stream

Erstellt von scarpall vor 10 Jahren Letzter Beitrag vor 10 Jahren 2.846 Views
S
scarpall Themenstarter:in
42 Beiträge seit 2012
vor 10 Jahren
Ordner Rekursiv auf NAS schreiben mit Progressbar mittels Stream

Hallo,

Also mit meinem Programm soll man einen Ordner mit Unterordnern auf einem Netzwerkspeicher abspeichern können.
Ich möchte das ganze mit einem Stream realisieren da das soweit ich bis jetzt in Erfahrung bringen konnte nur mit einem Stream geht.
Falls es auch anders geht könnt ihr mir auch gerne alternativen nennen (wenn möglich auch mit Beispielen).

Aktueller Stand ist die Funktion Copy Dir und Copy (Copy verwende ich um einzelne Dateien zu kopieren und die funktioniert so auch komplett):

P.S.: Die Funktionen werden in einem Backgroundworker aufgerufen der so aufgebaut ist:

Backgroundworker:


void worker_DoWork(object sender, DoWorkEventArgs e)
        {
            if (Datei == true)
            {
                DirectoryInfo dirinfo = new DirectoryInfo(pfad.Substring(0, pfad.Length - openFileDialog1.SafeFileName.Length));
                    Stream inStream = File.Open(pfad, FileMode.Open);
                    Stream outStream = File.Create(@"NETZWERKPFAD" + comboboxselecteditem + "\\" + openFileDialog1.SafeFileName);
                    int prozentfertig = (int)e.Argument;
                    while (prozentfertig < 100)
                    {
                        prozentfertig++;
                        Copy(inStream, outStream);
                        worker.ReportProgress(prozentfertig);
                        Thread.Sleep(50);
                    }
                    inStream.Close();
                    inStream.Dispose();
                    outStream.Close();
                    outStream.Dispose();
                    dateien.Add(pfad);
                    e.Result = prozentfertig;
            }
            else
            {
                DirectoryInfo di = new DirectoryInfo(pfad);
                CopyDir(pfad, @"NETZWERKPFAD" + comboboxselecteditem + "\\" + di.Name + "\\");
            }
        }

Worker Progress Changed:


        void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            progressBar1.Value = e.ProgressPercentage;
        }

Copy:


public void Copy(Stream input, Stream output)
        {
            byte[] buffer = new byte[32 * 1024];
            int bytesRead;
            while ((bytesRead = input.Read(buffer, 0, buffer.Length)) != 0)
            {
                output.Write(buffer, 0, bytesRead);
            }
        }

Meine Frage an euch wäre jetzt wie ich die Übertragung von Ordner schreiben kann.
Ich hatte bis jetzt versucht es mit der Copy Funktion hinzu bekommen indem ich das Verzeichnis Rekursive durchsuchen lasse und dann in einer foreach-Schleife die Copy Funktion verwende.

Das funktioniert soweit auch ganz gut nur das die Progressbar sich nicht aktualisiert (Die Progressbar wurde Invoked).

Hoffentlich könnt ihr mir helfen
MfG Scarpall

C
2.121 Beiträge seit 2010
vor 10 Jahren

Die while Schleife sieht seltsam aus. Du kopierst da immer das gleiche, so lange bis prozentfertig == 100?

S
scarpall Themenstarter:in
42 Beiträge seit 2012
vor 10 Jahren

Ja das erschien mir auchseltsam
Aber es funktioniert und sah für mich nicht so aus als ob etwas überschrieben werden würde

MfG scarpall

D
500 Beiträge seit 2007
vor 10 Jahren

Hi!

Das funktioniert soweit auch ganz gut nur das die Progressbar sich nicht aktualisiert (Die Progressbar wurde Invoked).

Siehe hier:
Backgroundworker

Hast du bspw. das Property WorkerSupportsCancellation auf true gesetzt?

Gruss,
DaMoe

Edit: Ich meinte natuerlich WorkerReportsProgress und nicht WorkerSupportsCancellation.

16.835 Beiträge seit 2008
vor 10 Jahren
  1. Wenn Du den Stream nur nutzt, damit Du einen Progress hin bekommst, nimm besser die Win32 API und CopyFileEx. Das kann Dir genauso den Progress anzeigen arbeitet aber schneller und zuverlässiger.
  2. Was soll das Sleep?
  3. Dein While sollte immer die Rest-Bytes prüfen, nicht den Prozent-Satz. Sehr fehleranfällig. Dass Du den Zustand des übertragenen extern speicherst (bytesRead) kann nach hinten los gehen. Code wirklich an dieser sehr wichtigen Stelle deutlich lesbarer und wartbarer machen. Das ganze Ding ist auch so nicht testbar! Und sowas MUSS testbar sein.
  4. Trenne das Suchen von Dateien und Kopieren von Dateien. Macht alles seeeehr viel übersichtlicher. Lager das ganze in eine eigene Klasse aus und arbeite mit Events, die Dich über den Status informieren. Zusätzlich würde ich mit einer BlockingCollection und mehreren Tasks arbeiten. Macht das ganze erheblich schneller (beachte aber das Abfangen von SystemBusyException) (Achtung: nur bei Netzwerk-Zugriffen, nicht lokal!!)
    5 Verwende usings und lade so viel wie möglich in dem RAM vor.
  5. öffne Dateien in so einem Fall immer Shared und nicht exklusiv. Ansonsten sind große Dateien eewwwig geblockt.
  6. Gerade bei Windows Shares sollte man Dein Vorhaben eher vermeiden oder zumindest deutlich optimierter arbeiten (größere Byte Pakete, um SMB Overhead zu vermeiden).

Wie Du Deine Progress-Anzeige für Deine Ordner-Kopieren-Methode erstellst ist im Prinzip Deine Sache; das machen die meisten Anwendungen jedes mal anders. Einige zeigen auch nur das Kopieren von Dateien an.

T
2.224 Beiträge seit 2008
vor 10 Jahren

@scarpall
Ganz einfach wäre es auch mit File.Copy gegangen.
Das kopiert auch über Netzwerkfreigaben.

Dann musst du dich nicht mit dem ganzen Bytes/Stream rumschlagen.
Ansonsten sagt Abt soweit alles was du noch optimieren solltest.

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

16.835 Beiträge seit 2008
vor 10 Jahren

Er will aber ein Progress und das geht bei File.Copy nicht Byte-weise, sondern nur nach Anzahl der Dateien.

3.825 Beiträge seit 2006
vor 10 Jahren

Ich kopiere die Dateien auch blockweise, da ich dann :

  • einen Progress anzeigen kann
  • einen Abbruch mitbekomme
  • Den Benutzer den Kopiervorgang abbrechen lassen kann.

Die Geschwindigkeit vom File.Copy ist identisch zum blockweise Kopieren.

Hier noch eine Anmerkung zum Windows File-Copy-Dialog:

The author of the Windows file copy dialogue visits some friends

Grüße Bernd

Workshop : Datenbanken mit ADO.NET
Xamarin Mobile App : Finderwille Einsatz App
Unternehmenssoftware : Quasar-3

S
scarpall Themenstarter:in
42 Beiträge seit 2012
vor 10 Jahren

Hallo

schon mal ein herzliches Dankeschön an alle die hier geantwortet haben.
Ich habe nochmal einiges bearbeitet so das dies der aktuelle Stand ist.

CopyDir:


DirectoryInfo dirinfo = new DirectoryInfo(pfad);

            if (!Directory.Exists(ziel))
                Directory.CreateDirectory(ziel);

            foreach (FileInfo fi in dirinfo.GetFiles())
            {
                CopyFile(dirinfo.FullName + "\\" + fi.Name, ziel + "\\" + fi.Name);
            }

            foreach (DirectoryInfo di in dirinfo.GetDirectories())
            {
                CreateFolders(di.FullName, ziel + "\\" + di.Name);
            }

CopyFile:


int bytesRead = 0;
            int bytesPerChunk = 1000001;

            using (FileStream fs = new FileStream(source, FileMode.Open, FileAccess.Read))
            {
                progressBar1.Parent.Invoke(new MethodInvoker(delegate { progressBar1.Maximum = (int)fs.Length; }));
                using (BinaryReader br = new BinaryReader(fs))
                {
                    using (FileStream fsDest = new FileStream(destination, FileMode.Create))
                    {
                        BinaryWriter bw = new BinaryWriter(fsDest);
                        byte[] buffer;

                        for (int i = 0; i < fs.Length; i += bytesPerChunk)
                        {
                            while(bytesPerChunk > (fs.Length - i))
                            {
                                bytesPerChunk -= 10;
                            }
                            buffer = br.ReadBytes(bytesPerChunk);
                            bw.Write(buffer);
                            bytesRead += bytesPerChunk;
                            worker.ReportProgress(bytesRead, fs.Length);
                        }
                    }
                }
            }

Nun funktioniert die Progressbar auch.
Einziges Problem ist das ich beim Kopieren von größeren Dateien den Fehler bekomme das der Wert zu groß für Progressbar.Maximum ist.

Mir ist auch klar das es sich bei diesem Wert um einen Integer handelt und dieser zum Beispiel nicht die Bytes einer z.B. 2 GB grpßen Imagedatei speichern kann.

Ich weiß im moment nur nicht wie ich den Code umändern muss so das:

  1. der wert für progressbar.maximum nicht überschritten wird und
  2. die Progressbar trotzdem Proportional zum kopieren mit läuft;

@Abt:

  1. Wenn Du den Stream nur nutzt, damit Du einen Progress hin bekommst, nimm besser die Win32 API und CopyFileEx. Das kann Dir genauso den Progress anzeigen arbeitet aber schneller und zuverlässiger.

Danke für den Tipp ich schaue es mir momentan an arbeite aber noch nicht damit weil ich vorher noch nie mit einer API gearbeitet habe.

  1. Was soll das Sleep?

Hatte ich nur eingefügt weil das in einem MSDN Beispiel auch drin war.

  1. Dein While sollte immer die Rest-Bytes prüfen, nicht den Prozent-Satz. Sehr fehleranfällig. Dass Du den Zustand des übertragenen extern speicherst (bytesRead) kann nach hinten los gehen. Code wirklich an dieser sehr wichtigen Stelle deutlich lesbarer und wartbarer machen. Das ganze Ding ist auch so nicht testbar! Und sowas MUSS testbar sein.

Auch diesen Tipp habe ich dankend übernommen.

5 Verwende usings und lade so viel wie möglich in dem RAM vor.

die usings habe ich nun verwendet. Wie genau meinst du das mit viel in den RAM vorladen bzw. wie kann ich das denn machen?

  1. öffne Dateien in so einem Fall immer Shared und nicht exklusiv. Ansonsten sind große Dateien eewwwig geblockt.

Wie kann ich das denn Shared öffnen?
Sorry für solche Fragen ich bin kein gelernter Programmieren hatte nur damals in der Ausbildungszeit 2 oder 3 Programmierlehrgänge und hatte festgestellt das es mir Spaß macht.
Den Rest hatte ich mir dann selber beigebracht.

  1. Gerade bei Windows Shares sollte man Dein Vorhaben eher vermeiden oder zumindest deutlich optimierter arbeiten (größere Byte Pakete, um SMB Overhead zu vermeiden).

die Byte Pakete habe ich vergrößert (auf 1000001 weil ich ja später die Pakete je nach restbytes um 10 reduziere so bleibt ja mindestens 1 byte am Ende übrig)

Edit:
Ich habe mir gerade ein Beispielprojekt für CopyFileEx runtergeladen.
Da sieht das button click event einfach so aus:


CopyFileEx(sourceFile, destFile, new CopyProgressRoutine(this.CopyProgressHandler), IntPtr.Zero, ref pbCancel, CopyFileFlags.COPY_FILE_RESTARTABLE);

Was müsste ich denn da umändern um es mit einer Progressbar verwenden zu können ?

Bzw. wie kann ich da die Übertragenen bytes etc auslesen ?

MfG Scarpall

16.835 Beiträge seit 2008
vor 10 Jahren

Wie genau meinst du das mit viel in den RAM vorladen bzw. wie kann ich das denn machen?

Ich habs selbst aus Interesse getestet: bringt übers lokale Netz nichts(/nicht viel).

Wie kann ich das denn Shared öffnen? File.Open-Methode (String, FileMode, FileAccess, FileShare) -> FileShare.

Ich habe mir gerade ein Beispielprojekt für CopyFileEx runtergeladen.
Da sieht das button click event einfach so aus:

  
CopyFileEx(sourceFile, destFile, new CopyProgressRoutine(this.CopyProgressHandler), IntPtr.Zero, ref pbCancel, CopyFileFlags.COPY_FILE_RESTARTABLE);  
  

Was müsste ich denn da umändern um es mit einer Progressbar verwenden zu können ?

Das ist nichts zum Ändern. Das ist die Win32 API Aufruf, die die Anwendung ansprechen muss. Arg toll ist dieses Beispiel aber nicht.
Ich habe gestern Abend diese Funktion noch in meine Lib integriert, die ich in Performant von vielen Dateien die Größe bestimmen angekündigt habe.
Im Lauf des Februars veröffentliche ich es dann kann man sehen, dass das gaaar nich so schwer ist. Brauchst eben einen Callback, der die Status-Benachrichtigungen entgegen nimmt.

T
415 Beiträge seit 2007
vor 10 Jahren