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

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

nebo

    1with TCompressionStream.Create(clMax, cílový stream) do
    2begin
    3  try
    4    CopyFrom(zdroj nepř. oFileStream, oFileStream.Size);
    5  finally
    6    Free;
    7  end;
    8end;

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.

    1program Project1;
    2
    3{$APPTYPE CONSOLE}
    4
    5uses
    6  SysUtils,
    7  sevenzip;
    8
    9var
   10  iTotal: int64;
   11
   12// jednoduchý výpis stavu, #8 je BACKSPACE, tj. neustále přepisuji sám sebe
   13 function ProgressCallback(sender: Pointer; total: boolean; value: int64): HRESULT; stdcall;
   14 begin
   15   if not total then
   16     write(Format('%6.2f%%'#8#8#8#8#8#8#8,  [100*value / iTotal]))
   17   else
   18     iTotal := value;
   19   Result := S_OK;
   20 end;
   21
   22procedure testcreate;
   23 var
   24   Arch: I7zOutArchive; // jedná se o interface, takže ho nemusíme uvolnit
   25 begin
   26   // typ archívu je ZIP
   27   Arch := CreateOutArchive(CLSID_CFormatZip);
   28   // přidáme soubor
   29   Arch.AddFile('c:\test.bin', 'folder\test.bin');
   30   // přidáme rekurzivně soubory
   31   Arch.AddFiles('c:\devkitpro\devkitPPC', 'folder', '*.*', true);
   32   // úroveň komprese
   33   SetCompressionLevel(Arch, 5);
   34   // kompresní metoda
   35   SetCompressionMethod(Arch, mzDeflate);
   36   // a zobrazování stavu
   37   Arch.SetProgressCallback(nil, ProgressCallback);
   38   Arch.SetPassword('heslo'); //heslo
   39   // a proveď vytvoření a uložení
   40   Arch.SaveToFile('c:\test.zip');
   41end;
   42
   43begin
   44  try
   45    testcreate;
   46  except
   47    on E: Exception do
   48      Writeln(E.ClassName, ': ', E.Message);
   49  end;
   50end.

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ářů.


Nabízíme Delphi školení na různá témata, primárně ve Vaší firmě.

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

Partial English version.

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 nebo burzy práce).

Pokud chcete podpořit tento server libovolnou částkou, můžete použít PayPal. Moc děkuji.

Delphi Certified Developer

O Delphi.cz

Delphi je jediný moderní RAD nástroj podporující tvorbu nativních aplikací pro platformu Win32, Win64 , Mac OSX a na iPhone a Android (s výhledem na další platformy díky FireMonkey) na současném trhu (včetně Windows 8.1).

V současnosti je světová komunita přes dva miliónů vývojářů.

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.

Anketa

Poslední komentáře

Comment RSS