Delphi7 - Come posso copiare un file che è in corso la scrittura
-
11-10-2019 - |
Domanda
Ho un'applicazione che registra le informazioni in un file di testo al giorno ogni secondo su un PC master. Un PC slave sulla rete utilizzando la stessa applicazione vorrebbe copiare questo file di testo alla sua unità locale. Posso vedere ci sta per essere problemi di accesso ai file.
Questi file non deve essere più grande di 30-40MB ciascuno. la rete sarà 100MB ethernet. Posso vedere esiste un potenziale per il processo di copia di prendere più di 1 secondo significa che il PC di registrazione sarà necessario aprire il file per la scrittura mentre è in corso la lettura.
Qual è il metodo migliore per la scrittura del file (logging) e la copia di file di procedure? So che c'è la procedura standard di Windows CopyFile (), tuttavia questo mi ha dato problemi di accesso ai file. C'è anche TFileStream utilizzando il flag fmShareDenyNone, ma questo anche molto di tanto in tanto mi dà un problema di accesso troppo (come 1 a settimana).
Che cosa è questo il modo migliore per realizzare questo compito?
Il mio attuale Registrazione del file:
procedure FSWriteline(Filename,Header,s : String);
var LogFile : TFileStream;
line : String;
begin
if not FileExists(filename) then
begin
LogFile := TFileStream.Create(FileName, fmCreate or fmShareDenyNone);
try
LogFile.Seek(0,soFromEnd);
line := Header + #13#10;
LogFile.Write(line[1],Length(line));
line := s + #13#10;
LogFile.Write(line[1],Length(line));
finally
logfile.Free;
end;
end else begin
line := s + #13#10;
Logfile:=tfilestream.Create(Filename,fmOpenWrite or fmShareDenyNone);
try
logfile.Seek(0,soFromEnd);
Logfile.Write(line[1], length(line));
finally
Logfile.free;
end;
end;
end;
La mia procedura di copia dei file:
procedure DoCopy(infile, Outfile : String);
begin
ForceDirectories(ExtractFilePath(outfile)); //ensure folder exists
if FileAge(inFile) = FileAge(OutFile) then Exit; //they are the same modified time
try
{ Open existing destination }
fo := TFileStream.Create(Outfile, fmOpenReadWrite or fmShareDenyNone);
fo.Position := 0;
except
{ otherwise Create destination }
fo := TFileStream.Create(OutFile, fmCreate or fmShareDenyNone);
end;
try
{ open source }
fi := TFileStream.Create(InFile, fmOpenRead or fmShareDenyNone);
try
cnt:= 0;
fi.Position := cnt;
max := fi.Size;
{start copying }
Repeat
dod := BLOCKSIZE; // Block size
if cnt+dod>max then dod := max-cnt;
if dod>0 then did := fo.CopyFrom(fi, dod);
cnt:=cnt+did;
Percent := Round(Cnt/Max*100);
until (dod=0)
finally
fi.free;
end;
finally
fo.free;
end;
end;
Soluzione
vorrei suggerire di non chiudere e riaprire il file condiviso più e più volte per cominciare. Dal momento che si scrive ad esso ogni secondo, che è solo in testa inutile.
Sul lato master, creare e chiudere il file (il flag fmCreate
non può essere utilizzato con altre bandiere!), Poi ri-aprirlo in modalità fmOpenWrite
con la condivisione fmShareDenyWrite
, lasciarla aperta, e scrivere quando necessario.
Sul lato slave, aprire il file in modalità fmOpenRead
con la condivisione fmShareDenyNone
, lasciarlo aperto, e leggere da esso ogni secondo. Non c'è bisogno di copiare l'intero file condiviso in rete ogni volta. Questo è larghezza di banda sprecata. Basta leggere ciò che i nuovi dati è stato scritto negli ultimi secondi e questo è tutto. Se lo slave ha bisogno dei dati da memorizzare in un file locale, allora si può gestire un file locale separata in modo indipendente del file condiviso, spingendo i nuovi dati nel file locale quando necessario.
Altri suggerimenti
Per gestire la sua specifica occasionale problema ricorrente:
non dire quale versione di Delphi che si sta utilizzando.
C'è un bug nel costruttore TFileStream.Create () fino al versione 2007 (almeno). Questo potrebbe spiegare i vostri problemi di concorrenza occasionali.
Detto questo, credo che il bug è più suscettibile di provocare i file non vengono creati come previsto (quando uno ShareMode è inoltre specificato), tho questo può allora a loro volta portano al tuo problema di concorrenza.
Un modo per aggirare questo potrebbe essere quando il file deve essere creato, creare innanzitutto il file poi semplicemente aprirlo per la scrittura come una chiamata del costruttore separato - questo in realtà rende la creazione di file una fase separata, con file di scrittura di una parte consistente della processo:
if not FileExists(filename) then
begin
// There may be a more efficient way of creating an empty file, but this
// illustrates the approach
LogFile := TFileStream.Create(FileName, fmCreate);
LogFile.Free;
line := Header + #13#10 + s + #13#10;
end
else
line := s + #13#10;
Logfile:=tfilestream.Create(Filename,fmOpenWrite or fmShareDenyNone);
try
logfile.Seek(0,soFromEnd);
Logfile.Write(line[1], length(line));
finally
Logfile.free;
end;
Utilizza lo standard Append creazione del file / comando di apertura, l'uso write
per aggiornare il registro, e close
immediatamente il file.
Utilizzare un lavoro sul sistema operativo per copiare / spostare i file; lo hanno riprovare e lanciare ad una maggiore frequenza di quello che si richiede.
Se si vuole fare dal di dentro Delphi quindi utilizzare MoveFile per spostare il tutto.
Si potrebbe desiderare di avvolgere entrambe le scritture di registro e le mosse in try-except
modo che possano essere processati nuovamente un numero ragionevole di volte se il file system (NTFS su Windows?) Non risolve il concorrenza per voi. Nel peggiore dei casi, sia:
- Il file ottenuto spostato e esso venga nuovamente e scritto.
- Il file non viene spostato subito perché è in fase di scrittura.
Se il sistema operativo non risolve la condizione di competizione, allora si dovrà dare priorità all'azione Starved utilizzando un semaforo / blocco.
la ricerca di una funzione chiamata "IsFileInUse" o qualcosa di simile, sono sicuro che è possibile utilizzare che, come:
// master
while IsFileInUse(*AFileName*) do
Sleep(10);
write-content-to-file
// slave
while IsFileInUse(*AFileName*) do
Sleep(10);
copy-file-to-a-special-location
e presto !! il gioco è fatto !!