2011-01-14 5 views
3

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

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

Каков наилучший метод для записи файлов (протоколирования) и копирования файлов? Я знаю, что существует стандартная процедура Windows CopyFile(), однако это дало мне проблемы с доступом к файлам. Существует также TFileStream с использованием флага fmShareDenyNone, но это также очень редко дает мне проблему с доступом (например, 1 раз в неделю).

Что это лучший способ выполнить эту задачу?

Мой текущий Logging Файл:

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; 

ответ

2

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

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

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

+0

Хорошее предложение для механизма регистрации - я обязательно реализую. В моей ситуации подчиненному устройству не нужно копировать файл каждую секунду, а не один раз в день, поэтому он проверяет измененное время двух файлов перед копированием. – Simon

+0

re fmCreate: Это один из недостатков реализации класса TFileStream. API Windows позволяет создавать файл с флагами без этого обходного пути. Я написал свой собственный класс для инкапсуляции: http://svn.berlios.de/svnroot/repos/dzchart/utilities/dzLib/trunk/src/u_dzFileStreams.pas – dummzeuch

+0

Ограничение было удалено в более поздних версиях VCL. Новые параметры были добавлены в конструктор FileCreate() и TFileStream, чтобы одновременно указать права доступа и права доступа. –

0

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

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

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

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

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

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

+0

Этот ответ содержит несколько разных плохих советов. –

+0

@ david -agreed. Использование TFile было первым, что я пробовал и сразу же столкнулся с проблемами. – Simon

1

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

Вы не говорите, какую версию 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; 
+0

Использование Delphi7 (его в названии :)). я рассмотрю эту ошибку создания. Благодаря! – Simon

+0

Ах да - как я этого не заметил? LOL – Deltics

-1

поиск функции, называемой «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 

и вуаля !! вы закончили!

+0

Этот ответ также не имеет заслуг. –

+0

@ Давид, у него проблемы с доступом к файлу, я не знаю, почему он не пишет прямо в пункт назначения, однако ему нужно назначить файл, написать и закрыть его, тем временем ему также нужно скопировать его для некоторых причина, он может заглянуть, пока файл не используется, и скопируйте его, anyhu. Я не вижу, чтобы вы давали какие-либо советы, я бы с удовольствием увидела его от вас :-P – ComputerSaysNo

+1

@Dorin У меня нет никакого совета. , поэтому я ничего не говорю. Но ваш подход не может работать. Ваш воображаемый IsFileInUse может False, но к тому моменту, когда процесс попытается открыть файл, он, возможно, уже был открыт другим процессом. –

Смежные вопросы