Dzielenie plików

Adam Boduch

Tematem tego gotowca będzie dzielenie plików. Czasem może się to przydac,
gdy np. piszesz program, który chcesz na przykład podzielić na dyskietki.
Wtedy trzeba podzielić program na porcje 1,38 MB. W takim wypadku znajdzie
zastosowanie nienijszy gotowiec.

Na samym początku trzeba określić bufor, czyli kawałki, którymi będziemy
kopiować fragmenty pliku. Bufor taki może np. wynosić 500, czyli 500
bajtów. W takim wypadku plik podzielimy fragmentami - każdy po 500 bajtów.
Oczywiście można określić większy bufor. Ja w przykładowym programie
umieściłem komponent TTrackBar, dzięki któremu sam użytkownik może określić
bufor.

W samym programie wykorzystamy strumienie TFileStream. W celu większego
zapoznania się z tym tematem poczytaj artykuł w dziale Delphi. Samo
załadowanie pliku do strumienia wygląda tak:

Input := TFileStream.Create(FileName, fmOpenRead);

"FileName" to ściezka do pliku. Następnie trzeba określić ile potrzebnych
będzie iteracji pętli, która rozdzieli plik:

for I := 0 to (Input.Size div BuffSize) do

"BuffSize" to bufor, a Input.Size to wielkość pliku. A w pętli po "trochu"
następuje czytanie określonej porcji danych równych BuffSize. Dane te są
kopiowanie do innego strumienia:

for I := 0 to (Input.Size div BuffSize) do
    begin
      Application.ProcessMessages;
      ProgressBar.Position := i;

    { za kazda iteracja petli - przesun sie w zawartosic pliku o rozmiarbufora }
      Input.Seek(i * BuffSize, soFromBeginning);

    { utworz w nowo utworzonym folderze plik odpowiadajacy fragmentowidzielonego pliku }
      Output := TFileStream.Create((DirPath + '\\' +ExtractFileName(FileName) + IntToStr(i) + '.temp'),
      fmCreate);
      try
      { nastepnie za pomoca funkcji CopyFrom ze strumienia zostajeprzekopiowana
        okreslona ilosc bajtow (bufor) do strumienia Output. Jezelipozostala do
        skopiowania czesc jest mniejsza od bufora to trzeba skopiowac tylkote
        czesc, ktora pozostala do skopiowania... :))
      }
        if (Input.Size - (i * BuffSize)) < BuffSize then
          Output.CopyFrom(Input, (Input.Size - (i * BuffSize)))
        else Output.CopyFrom(Input, BuffSize);
      finally
        Output.Free;
      end;
    end;   

W ten sposób zostaje tworzonych n plików, z których każdy plik ma rozmiar =
BuffSize.

Cała procedura do dzielenia plików wygląda tak:

procedure TMainForm.DivFile(const FileName: string);
var
  Input : TFileStream;
  Output : TFileStream;
  i : Integer;
  DirPath : string;
  BuffSize : Integer;
begin
  BuffSize := BufferTrack.Position; // pobierz rozmiar bufora ( rozmiarjednego pliku )
  DirPath :=  FileName + '.temp'; // dodaj rozszerzenie
  mkDir(DirPath);  // utworz folder
  Input := TFileStream.Create(FileName, fmOpenRead);
  try
    ProgressBar.Max := (Input.Size div BuffSize);

  { po podzieleniu rozmiaru pliku przez bufor daje to nam ilosc kawalkow zktorch
    skladal sie bedzie podzielony plik }
    for I := 0 to (Input.Size div BuffSize) do
    begin
      Application.ProcessMessages;
      ProgressBar.Position := i;

    { za kazda iteracja petli - przesun sie w zawartosic pliku o rozmiarbufora }
      Input.Seek(i * BuffSize, soFromBeginning);

    { utworz w nowo utworzonym folderze plik odpowiadajacy fragmentowidzielonego pliku }
      Output := TFileStream.Create((DirPath + '\\' +ExtractFileName(FileName) + IntToStr(i) + '.temp'),
      fmCreate);
      try
      { nastepnie za pomoca funkcji CopyFrom ze strumienia zostajeprzekopiowana
        okreslona ilosc bajtow (bufor) do strumienia Output. Jezelipozostala do
        skopiowania czesc jest mniejsza od bufora to trzeba skopiowac tylkote
        czesc, ktora pozostala do skopiowania... :))
      }
        if (Input.Size - (i * BuffSize)) < BuffSize then
          Output.CopyFrom(Input, (Input.Size - (i * BuffSize)))
        else Output.CopyFrom(Input, BuffSize);
      finally
        Output.Free;
      end;
    end;   
  finally
    Input.Free;
  end;  
end;

Tak przedstawia się procedura dzielenia pliku na mniejsze. A co z
połączeniem tych plików ponownie w jedną całość? Tutaj sprawa jest bardzo
podobna. Z tym, że z jakiegoś danego katalogu następuje odczytanie
wszystkich znajdujących się tam plików. Następnie pętla for po wszystkich
tych plikach. A w pętli kolejno następuje odczytanie zawartości pliku i
dodanie do strumienia głównego (strumień główny to łączony plik).

procedure TMainForm.ConnectFile(const Dir: string);
var
  SR : TSearchRec;
  Found : Integer;
  I : Integer;
  Input : TFileStream;
  Output : TfileStream;
  NumberOfFiles : Integer;
begin
  NumberOfFiles := 0;
{
  te instrukcje maja na celu uzyskanie ilosci plikow .temp znajdujacychsie
  w okreslonej lokalizacji - ilosc plikow oznacza zmienna NumberOfFile.
}
  Found := FindFirst(Dir + '\\*.temp', faAnyFile, SR);
  while Found = 0 do
  begin
    Inc(NumberOfFiles);
    Found := FindNext(SR);
  end;
  FindClose(SR);

{
   te instrukcje odpowiadaja za stworzenie pliku - to do niego zostaniewlaczona
   zawartosci pliczkow - kwalkow...
}
  if not FileExists(ExtractFileDir(Dir) +ChangeFileExt(ExtractFileName(Dir), '')) then
    Output := TFileStream.Create(ExtractFileDir(Dir) +ChangeFileExt(ExtractFileName(Dir), ''), fmCreate)
  else Output := TFileStream.Create(ExtractFileDir(Dir) +ChangeFileExt(ExtractFileName(Dir), ''), fmOpenWrite);

  ProgressBar.Max := NumberOfFiles;

  try
    for I := 0 to NumberOfFiles -1 do
    begin
      Application.ProcessMessages;
      ProgressBar.Position := i;
    { tutaj nastepuje otwarcie pliku-kawalka do skopiowania }
      Input := TFileStream.Create(Dir + '\\' +ExtractFileName(ChangeFileExt(DirListBox.Directory, '')) + IntToStr(i) +'.temp',
      fmOpenRead);
      try
      { tutaj do pliku laczonego kopiujemy zawartosc malego pliczku(czesci) }
        Output.CopyFrom(Input, Input.Size);
      finally
        Input.Free;
      end;
    end;
  finally
    Output.Free;
  end;
end;

Najlepiej, abyś sam zobaczył ten program w działaniu. Źródła możesz
ściągnąć ze strony http://4programmers.net/file.php?id=387

0 komentarzy