Komprese a Delphi

vložil Radek Červinka 20. dubna 2010 00:22

Místa není nikdy dost a proto může být vhodné aby náš program napsaný v Delphi umožňoval kompresovat uživatelská data.

Základem je se rozhodnout jakým způsobem budeme data pakovat, zda použijeme některý ze standardních formátů (ZIP, RAR, 7z) nebo budeme jen balit proud (stream) dat s případnou naší hlavičkou, v horším případě se pokusíme o vytvoření vlastního formátu (proč?).

Dle mé zkušenosti je výhodné použít kompresi streamu dat např. při ukládání větších dat do databáze a např. ZIP formát (případně s heslem) pro práci s více soubory.

Komprese streamu dat

Delphi od nepaměti obsahuje už v základní instalaci podporu pro práci s kompresními a dekompresními streamy díky Zlib (Jean-loup Gailly a Mark Adler) a to ve verzi 1.2.3 - viz. "Embarcadero\RAD Studio\7.0\source\Win32\rtl\common\ZLib.pas". Výsledkem je celkem kvalitní komprese, která na rozdíl od implementace od MS v .NET nevykazuje problémy s hůře komprimovatelnými soubory (pro .NET hledejte ManagedZLib.dll nebo ZLib.NET).

Pro použití přidejte do uses zlib a pak již můžete používat např. TDecompressionStream nebo TCompressionStream a jejich použití je celkem jasné. Základní je třeba něco takového

oDeflate := TDecompressionStream.Create(zdrojový stream, třeba soubor);
try
  oFileStream.CopyFrom(oDeflate, originální velikost souboru);
finally
  oDeflate.Free;
end;

nebo

with TCompressionStream.Create(clMax, cílový stream) do
begin
  try
    CopyFrom(zdroj nepř. oFileStream, oFileStream.Size);
  finally
    Free;
  end;
end;

Komprese souborů

Jsem zastáncem standardních kompresních formátů, takže se jich přidržím. Můžete začít např. příslušnou sekcí na torry.net, nebo zkusit slavnou TurboPower Abbrevia, uvolněnou jako Open Source, což je knihovna podporující PKZIP 4 (tj. ZIP), Microsoft CAB, TAR a gzip, a navíc samorozbalující archívy s podporou Delphi 6 - 2010 (aktuální verze je 3.05, Licence: MPL1.1).

Osobně jsem použití komponenty Abbrevia zvažoval, ale nakonec jsem se rozhodl pro knihovnu od 7z. Sice proti Abbrevii mám navíc DLL, ale zato větší podporu formátů, včetně 7z, 64bit ZIPu, větší rychlosti a velmi přehledného rozhraní.

Např. tento kompresní prográmek bežící v konzole.

program Project1;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  sevenzip;

var
  iTotal: int64;

// jednoduchý výpis stavu, #8 je BACKSPACE, tj. neustále přepisuji sám sebe
 function ProgressCallback(sender: Pointer; total: boolean; value: int64): HRESULT; stdcall;
 begin
   if not total then
     write(Format('%6.2f%%'#8#8#8#8#8#8#8,  [100*value / iTotal]))
   else
     iTotal := value;
   Result := S_OK;
 end;

procedure testcreate;
 var
   Arch: I7zOutArchive; // jedná se o interface, takže ho nemusíme uvolnit
 begin
   // typ archívu je ZIP
   Arch := CreateOutArchive(CLSID_CFormatZip);
   // přidáme soubor
   Arch.AddFile('c:\test.bin', 'folder\test.bin');
   // přidáme rekurzivně soubory
   Arch.AddFiles('c:\devkitpro\devkitPPC', 'folder', '*.*', true);
   // úroveň komprese
   SetCompressionLevel(Arch, 5);
   // kompresní metoda
   SetCompressionMethod(Arch, mzDeflate);
   // a zobrazování stavu
   Arch.SetProgressCallback(nil, ProgressCallback);
   Arch.SetPassword('heslo'); //heslo
   // a proveď vytvoření a uložení
   Arch.SaveToFile('c:\test.zip');
end;

begin
  try
    testcreate;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

Web: Delphi 7zip plugin API Licence: MPL1.1

Jo a pokud máte tip na nějakou dobrou ZIP komponentu, tak neváhejte a vložte odkaz do komentářů.

Tagy: ,

Komponenty | Návody

Komentáře

20.4.2011 11:06:45 #

Rss

Zdravim. Mam jeden problem s touhle komponentou na 7zip. Bez problemu s ni zabalim soubory do 7zip formatu, ale pouze v pripade, ze v ProgressCallback nepouziju vystup do progressbaru nebo do mema apod. pripadne jine komponenty na formu. V tom okamziku zustane vprogram zaseknuty a ja netusim vubec proc. Kdyz vystup na progressbar na formulari zakomentuju a zbytek v ProgressCallback necham, tak se vsechno provede jak ma.

Rss

20.4.2011 15:57:38 #

radekc

Funguje to bez problému.

function ProgressPackCallback(sender: Pointer; total: boolean; value: int64): HRESULT; stdcall;
begin
   frmMain.gUpdatePackStatus(total, value);
   Result := S_OK;
end;

Musí to být procedura, ne metoda a musí tam být stdcall.

radekc

21.4.2011 5:06:50 #

Rss

Predem se omlouvam, ale asi na me budes muset jak na vola :) Bohuzel z tve odpovedi jsem nepochopil, co presne mam udelat, protoze jsem v delphi teprve zacatecnik. Muzu poprosit o nejakej podrobnejsi priklad? Vychazim z tveho kodu a potrebuju zobrazit ukazatel prubehu komprimace na TProgressBar, ktery mam umisteny na Form1. Zkosel jsem vsechno mozne, googloval, ale k funkcnimu vysledku jsem se nedopracoval.

Rss

21.4.2011 9:43:51 #

radekc

function ProgressCallback(sender: Pointer; total: boolean; value: int64): HRESULT; stdcall;
begin
   if total then
     Form1.ProgressBar.Max := value
   else
     Form1.ProgressBar.Position := value;
   Result := S_OK;
end;


na strance je primo uvedeny priklad http://www.progdigy.com/?page_id=13
Ja uz nevim jak vice ti mam pomoci

Doufam, ze ten tvuj mail neni fake.

radekc

21.4.2011 14:13:28 #

Rss

Ehm ... email je bohuzel fake (emaily nepouzivam, ale ted uz tam mam funkcni). Co se tyce toho kodu, tak prave ze me nefunguje presne to, co je jak v prikladu na www.progdigy.com, tak u toho tveho clanku. Vysledek je v obou pripadech stejny ... program se "kousne" v momente, kdy ma provadet akci s progressbarem. Kdyz si necham vypisovat udaje misto do progressbaru do mema, tak se mi vypise jedno cislo a cele to zamrzne. Pouzivam Delphi 2009 a tady je ukazka meho kodu ...

function ProgressCallback(sender: Pointer; total: boolean; value: int64): HRESULT; stdcall;
begin
   if total then
     PatchMakerForm.ProgressBar.Max := value
   else
     PatchMakerForm.ProgressBar.Position := value;
   Result := S_OK;
end;

Arch := CreateOutArchive(CLSID_CFormat7z);
Arch.AddFile(ExtractFilePath(Application.ExeName)+filename+'.mwp', ExtractFilePath(Application.ExeName));
SetCompressionLevel(Arch, 5);
SevenZipSetCompressionMethod(Arch, m7BZip2);
Arch.SetProgressCallback(nil, ProgressCallback);
Arch.SaveToFile(ExtractFilePath(Application.ExeName)+filename+'.7z');

Rss

21.4.2011 14:26:14 #

radekc

zkus
function ProgressCallback(sender: Pointer; total: boolean; value: int64): HRESULT; stdcall;
begin
   if total then
     PatchMakerForm.ProgressBar.Max := value
   else
     PatchMakerForm.ProgressBar.Position := value;
   Application.ProcessMessages;
   Result := S_OK;
end;

On asi nezamrzne, jen se neprekresli.

radekc

21.4.2011 16:56:29 #

Rss

S tim jsem to zkousel taky. Jinak nejde o to, ze by zamrznul samotnej progressbar (prekreslovani). Ona zamrzne cela aplikace ... Po jejim nasilnem ukonceni dostanu napriklad soubor mwpatch_1.7zip, kterej ma delku 8 bytu a to je vsechno. Kazdopadne diky moc za pomoc. Po tom vsem trapeni jsem zkusil hledat dalsi komponenty a nasel jsem SevenZipVCL ... sice to neumi pracovat s novejsi verzi 7zipu, ale zase s tim nejsou takove problemy. Predpokladam, ze nejaka dalsi komponenta na praci s 7zip archivy, ktera by umela vyuzit vlastnosti 7zip verze 9.x neexituje.

Rss

21.4.2011 17:08:01 #

radekc

Zkus v JCL, JclCompression.pas pouziva 7Zip.

Ale je to zajimave, jelikoz zrovna ted v jednom projektu to pouzivam a funguje mi to.

radekc

21.4.2011 17:27:52 #

Rss

Divne to je ... proste vse funguje az do doby nez chci zobrazit ten progressbar.
Dik za tip. JCL se zatim vyhybam, protoze u nej ma problem s hledanim prikladu, jak jednotlive komponenty pouzit.

Rss

21.5.2011 11:53:11 #

Tomáš Jantač

A nepoužíváš ve svém programu nějaká vlákna? S popisovanými kompresními mechanizmy zatím nemám zkušenosti, nevím jak fungují uvnitř, ale z popisovaných příkladů použití usuzuji že sami o sobě vlákna nepoužívají. Pokud je ale funkce pro kompresi volána z vlákna mohlo by být i volání updatu progress baru prováděno v jeho kontextu a přístup k VCL komponentám pak může způsobit problémy.

Tomáš Jantač

Komentování ukončeno

Naše nabídka

MVP
Ing. Radek Červinka - Embarcadero MVP
profil na linkedin, Twitter:@delphicz

Nabízím placené poradenství a konzultace v oblasti programování a vývoje SW.
Dále nabízíme i vývoj speciálního software na zakázku.

Neváhejte nás kontaktovat (i ohledně reklamy).

love Delphi

O Delphi.cz

Delphi je moderní RAD nástroj podporující tvorbu nativních aplikací pro platformu Win32, Win64, Mac OSX, Linux a na iPhone a Android.

Delphi.cz je nezávislý portál pro uživatele Delphi. Portál není koncipován pro úplné začátečníky, i když i ti se zde nebudou nudit, ale spíše na programátory, kteří již něco znají a chtějí své znalosti dále rozvíjet a sledovat novinky.

Poslední komentáře

Comment RSS

Dle měsíců