Быстрое определение того, было ли изменено содержимое папки
-
27-09-2020 - |
Вопрос
Мне нужно определить, какие папки содержат файлы, которые были изменены "недавно" (в течение определенного интервала).Я замечаю, что метки даты папок, похоже, обновляются всякий раз, когда изменяется содержащийся в них файл, но это поведение не распространяется вверх по дереву, т.е.метка даты папки, содержащей папку, содержащую измененный файл, не обновляется.
Я могу работать с таким поведением, но я подозреваю, что это зависит от платформы/файловой системы/сети или локального диска и т.д.Я все равно хотел бы воспользоваться этим там, где мог, поэтому мне нужна логическая функция, возвращающая значение true, если платформа/диск, на котором запущено мое приложение, поддерживает такое поведение.
Я вполне счастлив пройтись по дереву.Чего я хочу избежать, так это необходимости выполнять findFirst / FindNext для каждого файла в каждой папке, чтобы увидеть, были ли какие-либо изменения (скажем) за последний день - если я смогу избежать этого для папок, у которых не изменены метки даты в течение последнего дня, это сэкономит много времени. время.
Нет правильного решения
Другие советы
Проверьте FindFirstChangeNotification
и FindNextChangeNotification
функции
другим вариантом является использование TJvChangeNotify
ДЖЕДАЙСКИЙ компонент.
дополнительно вы можете проверить эту ссылку
Решения, которые были опубликованы до сих пор, о получении уведомлений, как они происходят, и они будут хорошо работать для этой цели.Если вы хотите посмотреть в прошлое и посмотреть, когда что-то было в последнее время изменилось, в отличие от мониторинга его в режиме реального времени, то он получает удар.Я думаю, что нет никакого способа сделать это, за исключением рекурсивно в поисках дерева папки и проверяя datestamps.
Редактировать: В ответ на комментарий OP, да, он не похоже, что есть возможность настроить FindFirst / Findnext, чтобы только ударить каталоги, а не файлы.Но вы можете пропустить проверку дат на файлы с помощью этого фильтра: (SearchRec.Attr and SysUtils.faDirectory <> 0)
.Это должно немного ускорить вещи.Не проверяйте даты в файлах вообще.Вы, вероятно, все равно придется сканировать через все, хотя, поскольку API Windows не предоставляет никакого способа (что я знаю), чтобы только запросил папки, а не файлы.
Я написал код для этой цели для одного из моих проектов.Это использует функции API FindFirstChangenotification и FindNextChangenotification. Вот код (я удалил некоторые специфические части проекта):
/// <author> Ali Keshavarz </author>
/// <date> 2010/07/23 </date>
unit uFolderWatcherThread;
interface
uses
SysUtils, Windows, Classes, Generics.Collections;
type
TOnThreadFolderChange = procedure(Sender: TObject; PrevModificationTime, CurrModificationTime: TDateTime) of object;
TOnThreadError = procedure(Sender: TObject; const Msg: string; IsFatal: Boolean) of object;
TFolderWatcherThread = class(TThread)
private
class var TerminationEvent : THandle;
private
FPath : string;
FPrevModificationTime : TDateTime;
FLatestModification : TDateTime;
FOnFolderChange : TOnThreadFolderChange;
FOnError : TOnThreadError;
procedure DoOnFolderChange;
procedure DoOnError(const ErrorMsg: string; IsFatal: Boolean);
procedure HandleException(E: Exception);
protected
procedure Execute; override;
public
constructor Create(const FolderPath: string;
OnFolderChangeHandler: TOnThreadFolderChange;
OnErrorHandler: TOnThreadError);
destructor Destroy; override;
class procedure PulseTerminationEvent;
property Path: string read FPath;
property OnFolderChange: TOnThreadFolderChange read FOnFolderChange write FOnFolderChange;
property OnError: TOnThreadError read FOnError write FOnError;
end;
/// <summary>
/// Provides a list container for TFolderWatcherThread instances.
/// TFolderWatcherThreadList can own the objects, and terminate removed items
/// automatically. It also uses TFolderWatcherThread.TerminationEvent to unblock
/// waiting items if the thread is terminated but blocked by waiting on the
/// folder changes.
/// </summary>
TFolderWatcherThreadList = class(TObjectList<TFolderWatcherThread>)
protected
procedure Notify(const Value: TFolderWatcherThread; Action: TCollectionNotification); override;
end;
implementation
{ TFolderWatcherThread }
constructor TFolderWatcherThread.Create(const FolderPath: string;
OnFolderChangeHandler: TOnThreadFolderChange; OnErrorHandler: TOnThreadError);
begin
inherited Create(True);
FPath := FolderPath;
FOnFolderChange := OnFolderChangeHandler;
Start;
end;
destructor TFolderWatcherThread.Destroy;
begin
inherited;
end;
procedure TFolderWatcherThread.DoOnFolderChange;
begin
Queue(procedure
begin
if Assigned(FOnFolderChange) then
FOnFolderChange(Self, FPrevModificationTime, FLatestModification);
end);
end;
procedure TFolderWatcherThread.DoOnError(const ErrorMsg: string; IsFatal: Boolean);
begin
Synchronize(procedure
begin
if Assigned(Self.FOnError) then
FOnError(Self,ErrorMsg,IsFatal);
end);
end;
procedure TFolderWatcherThread.Execute;
var
NotifierFielter : Cardinal;
WaitResult : Cardinal;
WaitHandles : array[0..1] of THandle;
begin
try
NotifierFielter := FILE_NOTIFY_CHANGE_DIR_NAME +
FILE_NOTIFY_CHANGE_LAST_WRITE +
FILE_NOTIFY_CHANGE_FILE_NAME +
FILE_NOTIFY_CHANGE_ATTRIBUTES +
FILE_NOTIFY_CHANGE_SIZE;
WaitHandles[0] := FindFirstChangeNotification(PChar(FPath),True,NotifierFielter);
if WaitHandles[0] = INVALID_HANDLE_VALUE then
RaiseLastOSError;
try
WaitHandles[1] := TerminationEvent;
while not Terminated do
begin
//If owner list has created an event, then wait for both handles;
//otherwise, just wait for change notification handle.
if WaitHandles[1] > 0 then
//Wait for change notification in the folder, and event signaled by
//TWatcherThreads (owner list).
WaitResult := WaitForMultipleObjects(2,@WaitHandles,False,INFINITE)
else
//Wait just for change notification in the folder
WaitResult := WaitForSingleObject(WaitHandles[0],INFINITE);
case WaitResult of
//If a change in the monitored folder occured
WAIT_OBJECT_0 :
begin
// notifiy caller.
FLatestModification := Now;
DoOnFolderChange;
FPrevModificationTime := FLatestModification;
end;
//If event handle is signaled, let the loop to iterate, and check
//Terminated status.
WAIT_OBJECT_0 + 1: Continue;
end;
//Continue folder change notification job
if not FindNextChangeNotification(WaitHandles[0]) then
RaiseLastOSError;
end;
finally
FindCloseChangeNotification(WaitHandles[0]);
end;
except
on E: Exception do
HandleException(E);
end;
end;
procedure TFolderWatcherThread.HandleException(E: Exception);
begin
if E is EExternal then
begin
DoOnError(E.Message,True);
Terminate;
end
else
DoOnError(E.Message,False);
end;
class procedure TFolderWatcherThread.PulseTerminationEvent;
begin
/// All instances of TFolderChangeTracker which are waiting will be unblocked,
/// and blocked again immediately to check their Terminated property.
/// If an instance is terminated, then it will end its execution, and the rest
/// continue their work.
PulseEvent(TerminationEvent);
end;
{ TFolderWatcherThreadList }
procedure TFolderWatcherThreadList.Notify(const Value: TFolderWatcherThread;
Action: TCollectionNotification);
begin
if OwnsObjects and (Action = cnRemoved) then
begin
/// If the thread is running, terminate it, before freeing it.
Value.Terminate;
/// Pulse global termination event to all TFolderWatcherThread instances.
TFolderWatcherThread.PulseTerminationEvent;
Value.WaitFor;
end;
inherited;
end;
end.
.
Это обеспечивает два класса;Класс резьбы, который контролирует папку для изменений, и если обнаружено изменение, он будет возвращать текущее время изменений и предыдущее время изменений через событие на OnFolderChange.И класс списка для хранения списка потоков мониторинга.Этот список автоматически завершает каждую поток, когда поток удален из списка.
Я надеюсь, что это поможет вам.
Вы должны посмотреть на http://help.delphi-jedi.org / item.php? Id= 172977 , который является готовым решением. Если вы не хотите загружать и установить целую JVCL (однако, есть отличный кусок кода;)) Возможно, вы захотите увидеть источник файла онлайн - http://jvcl.svn.sourceforge.net/viewvc/jvcl/trunk/jvcl/run/jvchangeNotify.PAS? Revision= 12481 и AMP; View= Markup