繁体   English   中英

Delphi7 - 如何复制正在写入的文件

[英]Delphi7 - How can i copy a file that is being written to

我有一个应用程序,每秒在主PC上将信息记录到每日文本文件。 使用相同应用程序的网络上的从属PC希望将此文本文件复制到其本地驱动器。 我可以看到存在文件访问问题。

这些文件每个不应超过30-40MB。 网络将是100MB以太网。 我可以看到复制过程有可能花费超过1秒的时间,这意味着日志记录PC需要在读取文件时打开文件进行写入。

文件写入(日志记录)和文件复制过程的最佳方法是什么? 我知道有标准的Windows CopyFile()过程,但这给了我文件访问问题。 还有TFileStream使用fmShareDenyNone标志,但这也偶尔会给我一个访问问题(比如每周1次)。

这是完成这项任务的最佳方式是什么?

我目前的文件记录:

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;

我的文件复制程序:

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;

我建议不要一遍又一遍地关闭和重新打开共享文件。 既然你每秒都写到它,那就是不必要的开销。

在Master端,创建并关闭文件( fmCreate标志不能与其他标志一起使用!),然后在fmOpenWrite模式下使用fmShareDenyWrite共享重新打开它, fmOpenWrite打开状态,并在需要时写入。

在Slave端,使用fmShareDenyNone共享以fmOpenRead模式打开文件, fmOpenRead打开状态,并每秒读取一次。 无需每次都通过网络复制整个共享文件。 这是浪费的带宽。 只需读取过去几秒内写入的所有新数据即可。 如果Slave需要将数据存储在本地文件中,那么它可以独立于共享文件管理单独的本地文件,在需要时将新数据推送到本地文件中。

处理您特定的偶然重复出现的问题:

你没有说你正在使用什么版本的Delphi。

TFileStream.Create()构造函数中有一个错误,包括2007版本(至少)。 这可以解释您偶尔出现的并发问题。

话虽如此,我认为该错误更有可能导致文件未按预期创建(当另外指定ShareMode时),这可能反过来导致您的并发问题。

解决这个问题的一种方法可能是在需要创建文件时,首先创建文件然后只需打开它作为单独的构造函数调用 - 这实际上使文件创建成为一个单独的步骤,文件编写了流程的一致部分:

  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;

使用标准附加文件创建/打开命令,使用write更新日志,并立即close文件。

使用操作系统上的作业来复制/移动文件; 让它重试并以超出您要求的频率发射。

如果你想从Delphi中做到这一点,那么使用MoveFile来移动整个东西。

您可能希望将日志写入和移动都包装在try-except这样如果文件系统(Windows上的NTFS?)不能解析并发性,它们可以重试合理的次数。 在最坏的情况下,要么:

  1. 文件被移动并重新创建并写入。
  2. 由于正在写入文件,因此不会立即移动该文件。

如果操作系统无法解决竞争条件,则必须使用信号量/锁定优先考虑饥饿操作。

搜索一个名为“IsFileInUse”的函数或类似的东西,我相信你可以使用它:

// master
while IsFileInUse(*AFileName*) do
  Sleep(10);
write-content-to-file

// slave
while IsFileInUse(*AFileName*) do
  Sleep(10);
copy-file-to-a-special-location

和presto !! 你完成了!!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM