Delphi7 - Как я могу скопировать файл, в который записывается

У меня есть приложение, которое записывает информацию в ежедневный текстовый файл каждую секунду на главном ПК. Подчиненный ПК в сети, использующий то же приложение, хотел бы скопировать этот текстовый файл на свой локальный диск. Я вижу, будут проблемы с доступом к файлам.

Размер этих файлов не должен превышать 30-40 МБ каждый. сеть будет Ethernet 100 МБ. Я вижу, что процесс копирования может занять больше 1 секунды, что означает, что компьютер, ведущий журнал, должен будет открыть файл для записи во время его чтения.

Как лучше всего выполнять процедуры записи (журналирования) и копирования файлов? Я знаю, что существует стандартная процедура 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;

person Simon    schedule 14.01.2011    source источник


Ответы (3)


Я бы посоветовал сначала не закрывать и не открывать общий файл снова и снова. Поскольку вы пишете ему каждую секунду, это просто ненужные накладные расходы.

На стороне мастера создайте и закройте файл (флаг fmCreate нельзя использовать с другими флагами!), Затем повторно откройте его в режиме fmOpenWrite с fmShareDenyWrite общим доступом, оставьте его открытым и при необходимости напишите в него.

На стороне Slave откройте файл в fmOpenRead режиме с fmShareDenyNone общим доступом, оставьте его открытым и читайте из него каждую секунду. Нет необходимости каждый раз копировать весь общий файл по сети. Это потраченная впустую пропускная способность. Просто прочтите все новые данные, которые были записаны за последние несколько секунд, и все. Если ведомому устройству требуется, чтобы данные хранились в локальном файле, он может управлять отдельным локальным файлом независимо от общего файла, при необходимости помещая новые данные в локальный файл.

person Remy Lebeau    schedule 14.01.2011
comment
Хорошее предложение по механизму логирования - обязательно выполню. В моей ситуации подчиненному устройству не нужно копировать файл каждую секунду, скорее, один раз в день, поэтому он проверяет время изменения двух файлов перед копированием. - person Simon; 15.01.2011
comment
re fmCreate: Это один из недостатков реализации класса TFileStream. Windows API позволяет создать файл с флагами без этого обходного пути. Я написал свой собственный класс для инкапсуляции этого: svn .berlios.de / svnroot / repos / dzchart / utilities / dzLib / trunk / src /. - person dummzeuch; 15.01.2011
comment
Ограничение было снято в более поздних версиях VCL. В конструктор FileCreate () и TFileStream были добавлены новые параметры, позволяющие одновременно указывать права доступа и права совместного использования. - person Remy Lebeau; 16.01.2011

Чтобы справиться с вашей конкретной периодически повторяющейся проблемой:

Вы не говорите, какую версию 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;
person Deltics    schedule 14.01.2011
comment
Используя Delphi7 (это в заголовке :)). я изучу эту ошибку создания. Спасибо! - person Simon; 14.01.2011
comment
Ах да, как я этого не заметил? ржу не могу - person Deltics; 14.01.2011

Используйте стандартную команду создания / открытия файла Добавить, используйте write для обновления log и сразу close файл.

Используйте задание в операционной системе для копирования / перемещения файлов; попросите его повторить попытку и запустить его с частотой, превышающей требуемую.

Если вы хотите сделать это из Delphi, используйте MoveFile, чтобы переместить Все это.

Вы можете заключить и записи журнала, и перемещения в try-except, чтобы их можно было повторить разумное количество раз, если файловая система (NTFS в Windows?) Не разрешает параллелизм за вас. В худшем случае либо:

  1. Файл был перемещен, и он воссоздается и записывается в него.
  2. Файл не перемещается сразу, потому что в него выполняется запись.

Если ОС не разрешает состояние гонки, вам придется отдать приоритет действию с ограниченным доступом, используя семафор / блокировку.

person Apalala    schedule 14.01.2011
comment
Этот ответ содержит несколько разных плохих советов. - person David Heffernan; 14.01.2011
comment
@david - согласен. Использование TFile было первым, что я попробовал, и сразу же возникли проблемы. - person Simon; 15.01.2011