Delphi - Állománytömörítő komponens

A Delphi rejtett képességei 1. rész

forráskód letöltése
Kétrészes cikksorozatunkban ismét előtérbe helyezzük a Delphi azon rejtett képességét, melynek segítségével állományokat tömöríthetünk, hasonló hatásfokkal, mint egy WinZIP, vagy ARJ. Bizonyos esetekben ez a tömörítési arány jobb is az említett programokénál és sebességben sem marad el azoktól.
A cikksorozat mostani részében egy olyan komponenst készítünk melynek segítségével egy adott könyvtár akár összes állományát is tömöríthetjük. A sorozat második részében pedig elkészítjük azt a komponenst, mely ezeket a tömörített állományokat képes kicsomagolni. A mellékelt példaprogram megnyitása előtt a Compress.pas-ban lévő komponenst telepítenie kell a Delphi alá. Ahhoz, hogy ez a telepítés sikeres legyen, előbb szükséges még egy igen fontos lépés:
A Delphi telepítő CD-jén találhatóak azok a forráskódok, melyek a tömörítő algoritmust megvalósítják, így nekünk már csak ennek a felhasználását kell megismernünk.
Ahhoz, hogy használni tudjuk ezt a funkciót bármely alkalmazásunkban, először szükség lesz arra, hogy a CD-ről bemásoljuk ezeket az állományokat a megfelelő helyre.
A CD meghajtón a "D:\INFO\EXTRAS\ZLIB\OBJ" könyvtár teljes tartalmát másoljuk a "C:\Program Files\Borland\DelphiX\Lib" könyvtárba (ahol az X az adott Delphi verziószáma), valamint a "D:\INFO\EXTRAS\ZLIB"-ben található Zlib.pas és Zlib.dcu állományokat szintén az előbbi könyvtárba. A másolás elvégzése után ne felejtsük el, hogy amit CD-ről másolunk az ReadOnly attribútummal rendelkezik, így a Delphi ezeket nem tudja módosítani az újrafordítás során. Használat előtt célszerű a ReadOnly attribútumot törölni.

Mellékelt komponens készítésekor azt tapasztaltuk, hogy a Delphi 5-ös verziójához mellékelt ZLIB néhány esetben megbízhatatlanul végezte el a tömörített állomány kicsomagolását. Annak ellenére, hogy mind a Delphi 5, mind a Delphi 4 Zlib.pas állományába a ZLIB verziószáma 1.0.4. úgy tűnik, hogy az mégsem egyforma.
const
  zlib_Version = '1.0.4';
A Delphi 4-hez adott ZLIB használata esetén ez a hibajelenség megszűnt, még akkor is, ha Delphi 5 alól használtuk azt.

A komponens használatához a SourceDir property-ben adjuk meg azt a könyvtárat, melynek az állományait tömöríteni szeretnénk.

Az OutputDir property-ben adjuk meg azt a könyvtárat, melybe a tömörített állományokat szeretnénk elhelyezni.

A tömörítés mértékét és ez által annak sebességét a CompressionLevel property-n keresztül szabályozhatjuk. Ennek értékei az alábbiak lehetnek:
  • clNone - nincs tömörítés, csak másolás
  • clFastest - gyors tömörítés, kisebb hatásfokkal
  • clDefault - alapértelmezett tömörítés
  • clMax - legnagyobb hatásfokú tömörítés, mely időben tovább tart az előzőeknél

A tömörítési folyamat megkezdéséhez az Execute eljárást kell meghívni. Minden egyes állomány tömörítése úgy történik, hogy létrejön a megadott könyvtárba egy azonos nevű állomány, melynek kiterjesztése zlib lesz.

Az adott állomány tömörítése előtt jön létre az OnFilter esemény. Itt a FileName paraméterben megkapjuk az állomány nevét. Ha az Enabled paraméternek hamis értéket adunk, akkor az adott állomány nem kerül tömörítésre. Ennek segítségével tetszőleges szűrést végezhetünk, hogy melyik állomány legyen tömörítve és melyik ne.

Ha sok állományt tömörítünk és szeretnénk kijelezni ennek hosszas folyamatát a felhasználó felé, akkor az OnProgress eseményt használjuk fel.

Nézzük miként működik komponensünk. Az Execute eljárás meghívja a CompressOneDir belső eljárásunkat átadva paraméterként a tömörítendő alkönyvtárat.
procedure TCompress.Execute;
begin
  CompressOneDir(FSourceDir);
end;
A CompressOneDir a FindFirstFile - FindNextFile - FindClose függvények segítségével keresést végez a könyvtárban az összes állományra. Ha talál egy állományt, akkor hívja meg a CompressOneFile nevű belső eljárásunkat paraméterként a talált állomány nevével és elérési útvonalával.
procedure TCompress.CompressOneDir(source: string);
var
  h: THandle;
begin
  h:=FindFirstFile(PChar(source+'*.*'), FD);
  if h<>INVALID_HANDLE_VALUE then begin
    repeat
      if FD.cFileName[0]<>'.' then begin
        if not (FD.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY=
              FILE_ATTRIBUTE_DIRECTORY) then begin
          CompressOneFile(source+FD.cFileName);
        end;
      end;
    until not FindNextFile(h, FD);
    Windows.FindClose(h);
  end;
end;
A CompressOneFile eljárás lesz, amely elvégzi az adott állomány tömörítését. Ehhez kell felhasználnunk a Zlib.pas által létrehozott algoritmust, illetve objektumot. A tömörítéshez egy TCustomZlibStream nevű objektumra lesz szükség. Amikor ezt az objektumot létrehozzuk a Create konstruktorával, akkor első paraméterként a tömörítés mértékét kell megadnunk, melyet az FCompressionLevel-ből veszünk, míg másodikként egy olyan TStream-ből származó objektumot, mely tartalmazza azt az állományt, amely a tömörített állomány lesz.. Jelen esetben ehhez egy TFileStream-et használunk, mellyel létrehozunk egy új állományt, ZLIB kiterjesztéssel. Egy másik TFileStream-el megnyitjuk a forrás állományt, melyet tömöríteni szeretnénk. Ezek után a TCustomZlibStream osztály CopyFrom eljárásával egy egyszerű másolást hajtunk végre ebből a forrás állományba. Ennek futása közben azonban a TCustomZlibStream tömörítve fogja kiírni az adatokat az újonnan létrehozott állományba.
procedure TCompress.CompressOneFile(source: string);
var
  cs: TCustomZlibStream;
  fs, fd: TFileStream;
  b: boolean;
begin
  b:=true;
  if Assigned(FOnFilter) then begin
    FOnFilter(Self, source, b);
  end;
  if b then begin
    fs:=TFileStream.Create(source, fmOpenRead);
    fd:=TFileStream.Create(FOutputDir+ExtractFileName(source)+
          '.zlib', fmCreate);
    cs:=TCompressionStream.Create(FCompressionLevel, fd);
    (cs as TCompressionStream).OnProgress:=DoProgress;
    cs.CopyFrom(fs, 0);
    DoProgress(nil);
    cs.Free;
    fd.Free;
    fs.Free;
  end;
end;

A Delphi rejtett képességei cikksorozat