![]() |
FileExists/FileDate - Auslesen über Netzwerk langsam
Ich benutze aktuell folgenden Schnipsel, um das Änderungsdatum einer Datei herauszufinden:
Delphi-Quellcode:
Die Funktion scheint etwas lahm zu sein, wenn man viele Dateien prüft, die auf einer Festplatte im Netzwerk liegen.
function getFileLastModified(const aFileName: string): TDateTime;
var SR: TSearchRec; begin Result := 0; try FindFirst(aFileName, faAnyFile, SR); Result := SR.TimeStamp; except end; end; Gibt es da nichts Schnelleres? Speziell wenn aFileName nicht existiert ist das sehr langsam. Letztendlich bin ich mir nicht sicher, ob es vielleicht doch FileExists() ist, welches über Netzwerk langsam ist. |
AW: FileExists/FileDate - Auslesen ber Netzwerk langsam
Kannst du nicht die Dateien mit Findfirst und FindNext auslesen und nicht jedesmal mit deiner Funktion eine neue Suche starten, wo wieder jedes Mal Findfirst vorkommt?
Oder kann man die Dateien nicht mit einer generellen Suche Beispiel "*.txt" suchen? |
AW: FileExists/FileDate - Auslesen ber Netzwerk langsam
Die Dateiliste existiert bereits.
Erklärung: Verzeichnis Quelle hat: - Datei1 - Datei2 - Datei3 Verzeichnis Ziel (Festplatte im Netzwerk): - Datei1 Datei2 und - Datei3 fehlen im Netzwerk also. Jetzt wird durch die Quellliste iteriert: von 0 bis Listenende-1 tue - Wenn Quelle+DateiN neuer als Ziel+DateiN dann .... (IsFileANewerFileB) Die Vergleichsfunktion:
Delphi-Quellcode:
Und ich bin mir nicht sicher, was hier so extremst abbremst. Ich denke aber, es ist FileExists.
function IsFileANewerFileB(aFileA, aFileB: string): Boolean;
var bRes: Boolean; begin if (not FileExists(aFileB)) or (not FileExists(aFileA)) then bRes := True else bRes := getFileLastModified(aFileA) > functions.getFileLastModified(aFileB); Result := bRes; end; function getFileLastModified(const aFileName: string): TDateTime; var SR: TSearchRec; begin Result := 0; FindFirst(aFileName, faAnyFile, SR); Result := SR.TimeStamp; end; Denn wenn ich FileExists() weglasse und getFileLastModified auc, dann ist es superschnell. Ich schätze mal, dass FindFirst intern auch irgendwie FileExists aufruft. |
AW: FileExists/FileDate - Auslesen ber Netzwerk langsam
Also erst mal könnte man den leeren Exception Block in die Tonne kloppen.
1. Überflüssig, wenn so wieso leer. 2. FindFirst wirft keine Execption: ![]() Ergo total überflüssig. Aber ohne näher Angaben über das Netzwerk (Auslastung, Größe, Server, Freigaben, Samba? wird man nicht viel dazu sagen können. |
AW: FileExists/FileDate - Auslesen ber Netzwerk langsam
Ich würde es eher Spielerei nennen :lol:
Es ist eine ganz normale Western Digital Festplatte an einem USB-Port meines Routers. Wenn ich eine große Datei auf die Festplatte kopiere, so geschieht das mit den erwarteten 100 Megabit (~11 MByte sagt Windows) |
AW: FileExists/FileDate - Auslesen ber Netzwerk langsam
Also Delphi 7 kennt die Funktion FileAge, die Hilfe schreibt dazu:
Zitat:
Delphi-Quellcode:
function IsFileANewerFileB(aFileA, aFileB: string): Boolean;
begin Result := FileAge(aFileA) > FileAge(aFileB); end; |
AW: FileExists/FileDate - Auslesen ber Netzwerk langsam
Verstehe ich nicht. Egal ob FindFirst oder FileAge. Alles gleich langsam.
In 15 Sekunden prüft er ~2700 Dateien (Durchgänge). Wenn Quelle und Ziel eine fest im PC installierte Festplatte sind, dauert das ganze keine 3 Sekunden mit 10.000 Durchgängen. |
AW: FileExists/FileDate - Auslesen über Netzwerk langsam
Die Geschwindigkeit des Netzwerks spielt hier eine stark untergeordnete Rolle. Wichtig ist die Latenz, und die ist nunmal in einem Netzwerk um Faktoren größer als bei einer lokal verbauten Platte. Und wenn es dann noch ein Plasterouter ist, sieht die Welt eh wieder anders aus (lies: Latenz noch größer), denn das sind nunmal keine NAS oder ähnliche auf LAN und SMB optimierte Geräte.
Grüße Dalai |
AW: FileExists/FileDate - Auslesen über Netzwerk langsam
Nur wie machen das dann Programme wie FreeFileSync.
Mit dem Programm kann ich 10000 Dateien von meiner im PC befindlichen Festplatte mit 10000 Dateien von der Festplatte im Netzwerk in etwa 10 Sekunden vergleichen. Das Programm vergleicht auch Datum oder Inhalt. Genau wie ich. Nur ich bin irgendwie zu blöd :lol: |
AW: FileExists/FileDate - Auslesen über Netzwerk langsam
Zu FreeFileSync gibt es auch den Quelltext, schaue da doch mal rein, wie die das machen. Wenn ich das richtig weiß und auch richtig beobachtet habe gehen die bei FreeFileSync über mehrere Threads die parallel laufen.
|
AW: FileExists/FileDate - Auslesen über Netzwerk langsam
Mh ja ok. Ich arbeite die 10000 Dateien (eine StringListe) in nur in einem Thread ab.
Hier wäre jetzt die Parallel Library von XE7 schön denke ich. |
AW: FileExists/FileDate - Auslesen über Netzwerk langsam
Keinem ist das fehlende FindClose aufgefallen?
10.000 offene Such-Handlesmachen sich bestimmt gut. Nja, wenn es langsam ist, dann würde ich wenigstens die Dateiliste sortieren und Dateien im gleichen Verziechnis auch mit dem gleichen FindFirst+FindNext suchen. |
AW: FileExists/FileDate - Auslesen über Netzwerk langsam
Zitat:
Das Problem ist eher, dass der Datums-Vergleich von 10.000 Dateien über das Netzwerk komischerweise solange dauert. Zu den Handles: ich benutze mittlerweile folgende Anpassung:
Delphi-Quellcode:
function FileTimeToDateTime(FileTime: TFileTime): TDateTime;
var ModifiedTime: TFileTime; SystemTime: TSystemTime; begin Result := 0; if (FileTime.dwLowDateTime = 0) and (FileTime.dwHighDateTime = 0) then Exit; try FileTimeToLocalFileTime(FileTime, ModifiedTime); FileTimeToSystemTime(ModifiedTime, SystemTime); Result := SystemTimeToDateTime(SystemTime); except Result := Now; // Something to return in case of error end; end; function getFileLastModified(const aFileName: string): TDateTime; begin Result := FileTimeToDateTime(JclFileUtils.GetFileLastWrite(aFileName)); end; |
AW: FileExists/FileDate - Auslesen über Netzwerk langsam
Es wird nie eine Exception auftreten. Guck doch mal in der Doku zu den Funktionen, die du nutzt. API Funktionen werfen keine Exceptions, sondern habe Rückgabewerte.
|
AW: FileExists/FileDate - Auslesen über Netzwerk langsam
Ist abgeändert.
Das Hauptproblem besteht aber noch immer und ich krieg nicht raus warum :( Es liegt aber definitiv an FileExists und dem netzwerk. Andere Programme sind da extrem viel schneller. |
AW: FileExists/FileDate - Auslesen über Netzwerk langsam
Zitat:
|
AW: FileExists/FileDate - Auslesen ber Netzwerk langsam
Zitat:
Gibt es beim USB-Port auch eine Möglichkeit zu wählen, ob "schnelles Entfernen" oder "mit Cache"? Das macht einen Unterschied um den Faktor 3. |
AW: FileExists/FileDate - Auslesen über Netzwerk langsam
Das ganze ist sehr einfach:
Andere Tools (auch meine) gehen die beiden Dateilisten einmal durch, speichern die Daten alle und vergleichen danach. Daher ist nur ein Abruf der Dateiliste nötig und die Dateidaten (Dateiname, -größe, -datum) sind ja in dieser Liste mit drin. Du scheinst hingegen zuerst die Liste abzurufen und dann für jede Datei noch einmal die Dateiliste anzufragen um die Datumsinformationen zubekommen, zu fragen ob sie existiert, ... das dauert ewig. |
AW: FileExists/FileDate - Auslesen über Netzwerk langsam
Zitat:
Bei mir ist es so: ich gehe die Liste der Quelldaten einmal durch. Dann wird immer geguckt: - "Gibt es Quell-DateiX auch im Zielverzeichnis? (FileExists) - JA > Quelldatei mit Zieldatei vergleichen (Datum) - Quelldatei neuer als Zieldatei? JA > in eine andere Liste schreiben (keine StringList...) Lasse ich FileExists und den Datumsvergleich weg, ist alles wunderbar zackig schnell. Seltsam ist ja nur.. es ist NUR von PC-HDD zu Netzwerk-HDD langsam. |
AW: FileExists/FileDate - Auslesen über Netzwerk langsam
Zitat:
|
AW: FileExists/FileDate - Auslesen über Netzwerk langsam
1. Daten sammeln (Dateien einmal scannen und die Daten in eine Liste ablegen)
2. Gesammelten Daten vergleichen und zu kopierende Dateien in einer Liste ablegen 3. Dateien nach der Liste kopieren Was du machst: 1. Einzelne Datei scannen 2. Vergleichen 3. Kopieren Es muss also für jede einzelne Datei alles immer wieder neu initialisiert werden. |
AW: FileExists/FileDate - Auslesen über Netzwerk langsam
Zitat:
Ich sammle erst alle Daten (Quelle) und speichere sie in einer Liste (das geht schnell). Danach gehe ich die Liste durch und prüfe - gibt es Quell-Datei1 auch im Zielverzeichnis. ![]() Daraus baue ich eine neue Liste und die wird später abgearbeitet (Daten werden kopiert). Anders wüsste ich nicht, wie man Daten vergleichen soll. |
AW: FileExists/FileDate - Auslesen über Netzwerk langsam
Zitat:
Sammle die vorhandenen Infos aus dem Ziel und vergleiche diese mit dem Quellsystem. Bei Differenzen musst du dann reagieren und gegebenenfalls kopieren. |
AW: FileExists/FileDate - Auslesen über Netzwerk langsam
Guck mal mein Beispiel. Nicht das Sammeln der Daten ist das Problem :)
Kleines Beispiel. Es wurde alles weggelassen bis auf das, wo es hapert. Das hier braucht rund 23 Sekunden (D:\ = im PC, Y:\ = Festplatte am Router per USB 100 MBit): (keine der Dateien existiert)
Delphi-Quellcode:
Wenn Y:\ durch D:\ ausgetauscht wird, dauert das Ganze 50ms.
function FileTimeToDateTime(FileTime: TFileTime): TDateTime;
var ModifiedTime: TFileTime; SystemTime: TSystemTime; begin Result := 0; if (FileTime.dwLowDateTime = 0) and (FileTime.dwHighDateTime = 0) then Exit; FileTimeToLocalFileTime(FileTime, ModifiedTime); FileTimeToSystemTime(ModifiedTime, SystemTime); Result := SystemTimeToDateTime(SystemTime); end; function getFileLastModified(const aFileName: string): TDateTime; begin Result := FileTimeToDateTime(JclFileUtils.GetFileLastWrite(aFileName)); end; function IsFileANewerFileB(aFileA, aFileB: string): Boolean; var bRes: Boolean; begin if (not FileExists(aFileB)) or (not FileExists(aFileA)) then bRes := True else bRes := getFileLastModified(aFileA) > getFileLastModified(aFileB); Result := bRes; end; ///////////////////////////////////////////////////////////////////////////////// procedure TForm1.Button1Click(Sender: TObject); var freq: Int64; startTime: Int64; endTime: Int64; i: Integer; a, b: String; begin QueryPerformanceFrequency(freq); QueryPerformanceCounter(startTime); a := 'D:\Datei1.txt'; b := 'Y:\Datei2.txt'; for i := 0 to 9999 do begin if IsFileANewerFileB(a, b) then begin end; end; QueryPerformanceCounter(endTime); ShowMessage('Die Routine benötigte etwa ' + IntToStr((endTime - startTime) * 1000 div freq) + 'ms'); end; ///////////////////////////////////////////////////////////////////////////////// |
AW: FileExists/FileDate - Auslesen über Netzwerk langsam
Du solltest einen kompletten Scan auf dem Quellsystem (FindFirst, FindNext, FindClose) machen und das gleiche auf dem Zielsystem. Dann hast du 2 Listen, die du vergleichen kannst. Dann musst du entscheiden, was gemacht werden soll.
Im Moment machst du das mit jeder Datei einzeln, FileExists, FileTimeToDateTime etc. Das wird dir wohl die Zeit kosten. Nimm doch einmal FindFirst, FindNext, FindClose und prüfe deine Zeit lokal und im Netz. Das andere ist nur Rechnerei auf deinem PC, was nicht die Welt an Zeit kosten wird. |
AW: FileExists/FileDate - Auslesen über Netzwerk langsam
Ich verstehe diesen Ansatz nicht so ganz.
Wenn ich das so mache wie du sagst, dann muss ich die Dateien ja trotzdem gegenprüfen und gucken, ob DateiX im Zielverzeichnis existiert usw. |
AW: FileExists/FileDate - Auslesen über Netzwerk langsam
Zitat:
Wie gesagt, Quelle scannen in eine Liste, Ziel scannen in eine weitere Liste und die Listen dann gegeneinander abgleichen. Dann hast du nur 1x einen scan in der Quelle und ein scan im Ziel. Hier ein Beispiel mit Suche von Verzeichnissen. In der StringList (SL) stehen dann erst einmal die Dateinamen. Gegebenenfalls noch das Datum mit dranhängen oder anderweitig speichern. Das machst du in der Quelle und im Ziel.
Delphi-Quellcode:
if SysUtils.FindFirst(Dir + '*.*', SysUtils.faDirectory, SR2) = 0 then
repeat if ((SR2.attr and faDirectory) = faDirectory) then SL.Add(); until SysUtils.FindNext(SR2) <> 0; finally FindClose(SR2); end; |
AW: FileExists/FileDate - Auslesen über Netzwerk langsam
Jetzt hab ich es endlich kapiert :wall:
Also von der Logik her ungefähr so? - erstelle Liste "Quelle" - erstelle Liste "Ziel" - iteriere durch Liste "Quelle" -- prüfe, z.B. mit IndexOf (StringList), ob Eintrag N der Liste "Quelle" in Liste "Ziel" ist --- wenn ja > weitere Prüfungen (Datumsvergleich) Bitte sag mir, dass ich es kapiert habe :stupid: |
AW: FileExists/FileDate - Auslesen über Netzwerk langsam
Zitat:
|
AW: FileExists/FileDate - Auslesen über Netzwerk langsam
Die Listen enthalten natürlich die erforderlichen Daten zum Vergleichen. Entweder ein Array mit Records oder besser eine Objektliste.
|
AW: FileExists/FileDate - Auslesen über Netzwerk langsam
Ist das hier auch OK?
Delphi-Quellcode:
Ein erster Test:
// ...
if WithSubDirs then sFileDate := ',' + FloatToStr(SR.TimeStamp) else sFileDate := ''; List.Add(SR.Name + sFileDate); // Ich will nur die Dateinamen mit SubbDir in der Liste haben (SR.Name) // ... - Ein Vergleich von 10.000 Dateien im Quell- UND Zielverzeichnis dauert mit "sFileDate" da oben ~ 24 bis 26 Sekunden. - mit dem Vergleich "if getFileLastModified(aFileA) > getFileLastModified(aFileB) then" in etwa 130 Sekunden. Denkt ihr, 24 Sekunden sind erträglich? Bedenke: Quell-Verzeichnis ist die HDD im PC, Zielverzeichnis die HDD am Router (also langsam). Von C nach D gehts schneller (~12 Sekunden). |
AW: FileExists/FileDate - Auslesen über Netzwerk langsam
Und für den Vergleich puzzelst du den String wieder auseinander? Nimm ein Records, die du in einer Lsite verwaltest oder eine Obejctlist. Dann wird es sauber.
|
AW: FileExists/FileDate - Auslesen über Netzwerk langsam
Ich habe gerade eine schöne Lösung mit einem TDynArray und einem Record gefunden. Aber ich finde ums Verrecken nicht, wo TDynArray deklariert ist.
Ok das lasse ich lieber sein. Das wirft mir alles durcheinander. Ich versuche das mal mit einem Record und einer ObjectList. |
AW: FileExists/FileDate - Auslesen über Netzwerk langsam
Zitat:
Delphi-Quellcode:
findest du viele Arraytypen. Aber bastle dir doch dein eigenes dynamisches Array.
System.Types
|
AW: FileExists/FileDate - Auslesen über Netzwerk langsam
Habe das gerade mal mit einem Record probiert, den ich in eine TStrings packe (AddObject).
Klappt alles wunderbar. Aber jetzt klappt IndexOf der Liste natürlich nicht mehr. Das fand ich recht angenehm vorher. Ich sehe gerade erst, dass IndexOf ja auch nicht klappt und immer -1 zurückgibt, wegen meiner SR.Name+Datum-Konstruktion. Edit: Ich hab es jetzt ganz schön (für mich) hinbekommen. Jetzt ist aber plötzlich das Problem aufgetreten, dass die Zieldaten, welche eine Kopie der Quelldaten sind, angeblich nicht gleich sind vom Datum her. Siehe hier: Zitat:
Edit #2: Problem hat sich von alleine gelöst. Ich glaube die Festplatte raucht ab. War vorherzusehen. |
AW: FileExists/FileDate - Auslesen über Netzwerk langsam
Irgendwie vergewaltigst du da gerade eine Stringlist. :roll: AdObjekct der Stringliste nimmt man zusätzlich zu den anzuzeigenden noch zusätzlich Daten speichern will.
Entweder dynamisches Array mit Records oder eine Objectlist mit Objekten. Mit welchen Dateisystemen haben wir es zu tun? FAT und NTFS habe unterschiedliche Auflösungen, was den Timestamp betrifft: ![]() |
AW: FileExists/FileDate - Auslesen über Netzwerk langsam
Beides NTFS.
Zur StringList. Ja ich weiß, nicht gut. Aber auf diese Art und Weise kann ich die Funktion ( GetFilesInDirectory ) mit und ohne Record benutzen:
Delphi-Quellcode:
Ich weiß. Der Variablenname ist total missverständlich aber so funktioniert es:
procedure GetFilesInDirectory(Directory: string; const Mask: string; List: TStrings; WithSubDirs, AsObjectList, ClearList: Boolean);
Delphi-Quellcode:
type
PFileListEntry = ^TFileListEntry; TFileListEntry = packed record sFileName: string; iFileSize: Int64; iFileDate: Extended; end; procedure GetFilesInDirectory(Directory: string; const Mask: string; List: TStrings; WithSubDirs, AsObjectList, ClearList: Boolean); var aFileListEntry: PFileListEntry; procedure ScanDir(const Directory: string); var SR: TSearchRec; begin if FindFirst(Directory + Mask, faAnyFile and not faDirectory, SR) = 0 then try repeat Application.ProcessMessages; /// ////////////////////////////////////////////////////////////////////////////////// if AsObjectList then begin System.New(aFileListEntry); aFileListEntry.sFileName := SR.Name; aFileListEntry.iFileSize := SR.Size; aFileListEntry.iFileDate := SR.TimeStamp; List.AddObject('F_' + SR.Name, Pointer(aFileListEntry)); end else begin List.Add(SR.Name); end; /// ////////////////////////////////////////////////////////////////////////////////// until FindNext(SR) <> 0; finally FindClose(SR); end; if WithSubDirs then begin if FindFirst(Directory + '*.*', faAnyFile, SR) = 0 then try repeat Application.ProcessMessages; if ((SR.attr and faDirectory) = faDirectory) and (SR.Name <> '.') and (SR.Name <> '..') then ScanDir(Directory + SR.Name + ''); until FindNext(SR) <> 0; finally FindClose(SR); end; end; end; begin List.BeginUpdate; try if ClearList then List.Clear; if Directory = '\' then Exit; if Directory[Length(Directory)] <> '\' then Directory := Directory + '\'; ScanDir(Directory); finally List.EndUpdate; end; end; function IndexOfListObjects(const s: string; List: TStringList): Integer; begin for Result := 0 to List.Count - 1 do if s = PFileListEntry(List.Objects[Result])^.sFileName then Exit; Result := -1; end; procedure TForm1.Button1Click(Sender: TObject); var freq: Int64; startTime: Int64; endTime: Int64; i, iDestFileIndex: Integer; sSourceDir, sDestDir: String; slSource, slDest: TStringList; aFileListEntrySource, aFileListEntryDest: TFileListEntry; begin QueryPerformanceFrequency(freq); QueryPerformanceCounter(startTime); slSource := TStringList.Create; slDest := TStringList.Create; try sSourceDir := 'D:\Test1\'; // behinhaltet 10.000 Dateien sDestDir := 'D:\Test2\'; // behinhaltet ebenfalls 10.000 Dateien, welche identisch sind GetFilesInDirectory(sSourceDir, '*.*', slSource, True, True, False); GetFilesInDirectory(sDestDir, '*.*', slDest, True, True, False); for i := 0 to slSource.Count - 1 do begin Application.ProcessMessages; aFileListEntrySource := PFileListEntry(slSource.Objects[i])^; iDestFileIndex := IndexOfListObjects(aFileListEntrySource.sFileName, slDest); if iDestFileIndex > -1 then begin aFileListEntryDest := PFileListEntry(slDest.Objects[iDestFileIndex])^; if aFileListEntrySource.iFileDate > aFileListEntryDest.iFileDate then begin // Tue was auch immer mit aFileListEntrySource end; end; end; // for finally for i := 0 to slSource.Count - 1 do Dispose(PFileListEntry(slSource.Objects[i])); for i := 0 to slDest.Count - 1 do Dispose(PFileListEntry(slDest.Objects[i])); slSource.Free; slDest.Free; end; QueryPerformanceCounter(endTime); showmessage('Die Routine benötigte etwa ' + IntToStr((endTime - startTime) * 1000 div freq) + 'ms'); end; |
AW: FileExists/FileDate - Auslesen über Netzwerk langsam
Timestamp ist garantiert nicht vom exotischen Typ Extended. Das sollte TDateTime sein.
Wenn du Zahlen in unterschiedliche Typen packst, hast du auch unterschiedliche Genauigkeiten und damit Rundungsfehler. Wann gibst du den Speicher für die Records eigentlich wieder frei? Und warum benutzt du nicht Klassen, die du ohne Pointerspielerei direkt in AddObject packen kannst? Und mit OwnsObjects auf True (Standard im Konstruktor) werden die auch automatisch wieder freigegeben. |
AW: FileExists/FileDate - Auslesen über Netzwerk langsam
Zitat:
Wa genau meinst du mit Klasse statt Pointer. Muss man den Speicher für die Records nicht wieder freigeben? Ich dachte, wenn ich datensätze erzeuge und ich sie verwendet habe, dass ich den Speicher freigeben muss. Habe jetzt jedenfalls von Extened to TDateTime geändert. Ich dachte vorher, ich nehme Extended weil es genauer ist. |
AW: FileExists/FileDate - Auslesen über Netzwerk langsam
Er meinst so was:
![]() |
Alle Zeitangaben in WEZ +1. Es ist jetzt 04:17 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz