|
|||||||||||||
|
|
||||||||
Vladimir Klisić | |||
Pod visokim pritiskom |
|||
Da li vam je ikada zatrebao mehanizam za arhiviranje podataka iz sopstvenog programa? Predstavljamo zaokruženo rešenje. |
Kada se krajem prošle decenije pojavio na tržištu uslužnih programa, PKZIP firme PKWare Inc. je bukvalno preko noći osvojio svet. Zahvaljujući kvalitetnoj kompresiji, pouzdanosti i odluci da se format Zip datoteke i ekstenzija .ZIP stave u javno vlasništvo, zip je ubrzo postao svetski standard za arhiviranje. Ipak, implementacija rada sa zip arhivama u komercijalnim programima morala je da sačeka zip kompatibilnu biblioteku rutina za kompresiju. Kada je 1995. godine grupa programera pod imenom Info-ZIP razvila prvu verziju zip kompatibilnog kompresora i dekompresora i stavila njegov izvorni kod u javno vlasništvo, prepreka više nije bilo... Polazna tačka
Vodeći autori iz ove grupe, Mark Adler i Jean-Loup Gailly, nastavili su razvoj u pravcu kreiranja biblioteke C rutina za kompresiju pod nazivom Zlib, takođe stavljene u javno vlasništvo. Zlib biblioteka predstavlja nepatentiranu implementaciju deflate algoritma za kompresiju, istog algoritma koji koristi i PKZip. Danas na tržištu gotovo da nema programa koji radi sa zip arhivama, a da ne koristi upravo nju, što nas dovodi do teme ovog teksta: kako raditi sa zip arhivama iz sopstvenog programa? Polaznu osnovu za implementaciju o kojoj će biti reči čine, dakle, specifikacija formata zip datoteke i Zlib biblioteka. Iako detalji implementacije podrazumevaju upotrebu C++-a i MFC-a, njeni opšti principi primenljivi su i na druge jezike i razvojna okruženja. Uprošćeno posmatrano, zip arhiva predstavlja prosti zbir blokova dobijenih kompresijom datoteka, pri čemu svakom bloku prethodi lokalno zaglavlje sa informacijama o datoteci koja je komprimovana. Na kraju arhive, iza svih ovako zabeleženih blokova, nalazi se centralni direktorijum, niz centralnih zaglavlja – po jedno za svaku datoteku u arhivi. Centralno zaglavlje sadrži sve informacije koje već postoje u odgovarajućem lokalnom zaglavlju, uz još neke podatke potrebne da se konkretni komprimovani blok locira u arhivi: tu su, između ostalog, oznaka diska na kome se nalazi i pozicija lokalnog zaglavlja u arhivi, merena od početka fajla. Centralni direktorijum omogućava da se informacije o strukturi arhive pročitaju efikasno, sa jednog mesta. Kao završni deo centralnog direktorijuma, iza niza centralnih zaglavlja, nalazi se blok sa osnovnim podacima – pozicija centralnog direktorijuma u arhivi, broj centralnih zaglavlja, veličina centralnog direktorijuma... Struktura zip datoteke je identična i u situaciji kada se arhiva prostire na više diskova: tada svaki disk (u suštini predstavljen po jednom datotekom) ima u svom centralnom direktorijumu zapisanu informaciju o rednom broju, rednom broju diska od kog počinje centralni direktorijum, broju centralnih zaglavlja zapisanih na taj disk i ukupnom broju centralnih zaglavlja... Za potrebe ovog teksta smatraćemo da se arhiva sastoji iz samo jedne fizičke datoteke (diska), što će uprostiti dalje razmatranje i implementaciju. Šta nam je činiti?Minimalna implementacija mora da obezbedi kreiranje zip arhive (minimalna Zip arhiva ima samo centralni direktorijum u kojem nema centralnih zaglavlja, i ne sadrži nijednu komprimovanu datoteku), listanje sadržaja arhive, dodavanje datoteke u arhivu, otpakivanje datoteke iz arhive i brisanje datoteka iz arhive. Implementacija o kojoj će biti reči definiše samo dve klase: CZipArchive i CZipFile. CZipArchive objekat opisuje arhivu u kojoj su komprimovane datoteke predstavljene nizom CZipFile objekata. CZipFile objekat čuva detalje vezane za datoteku koja je u arhivi: njeno ime, datum i vreme kreiranja, atribute, originalnu dužinu, dužinu u komprimovanom stanju, CRC i eventualni komentar. I interfejs ovog objekta je trivijalan: svodi se na čitanje i izmenu sadržaja članova klase. Sa druge strane, CZipArchive objekat sadrži celokupnu potrebnu funkcionalnost za manipulaciju arhivom; između ostalog, on održava listu CZipFile objekata, alocira bafere potrebne za kompresiju i dekompresiju i upravlja datotekom koja na disku fizički predstavlja arhivu. Za kompresiju datoteke, CZipArchive implementira funkciju CompressFile() koja je malo više od ”omotača” oko elementarne funkcije deflate() iz Zlib biblioteke. CompressFile() objedinjuje kreiranje lokalnog zaglavlja i proces kompresije, generišući CRC datoteke potrebne za proveru ispravnosti sadržaja prilikom otpakivanja. Sa druge strane, funkcija UncompressFile() pre dekompresije proverava konzistentnost lokalnog zaglavlja, da bi nakon uspešnog otpakivanja kompletirala rekonstrukciju originalne datoteke podešavanjem datuma kreiranja i atributa. Ove funkcije su deo interne implementacije i kao takve nisu vidljive za nekoga ko koristi klasu. Dodavanje datoteka u arhivu i brisanje iz arhive zahteva izvesnu količinu dodatnog koda koji se bavi ažuriranjem strukture arhive. Dodavanje datoteke u arhivu lepo ilustruje šta se sve tom prilikom mora uraditi. U prvoj varijanti, datoteka se ne nalazi u arhivi: na kraj arhive, počev od mesta gde je centralni direktorijum, dodaje se upravo komprimirana datoteka i zapisuje novi centralni direktorijum. U drugoj varijanti datoteka se već nalazi u arhivi: ako je fizički poslednja u arhivi, jednostavno se prepisuje novim sadržajem počev od svoje lokacije i potom se zapisuje novi centralni direktorijum. Situacija se komplikuje kada iza datoteke u arhivi ima još datoteka. Tada se ažuriranje izvodi iz četiri koraka: najpre se u privremeni fajl kopiraju sve datoteke koje su u arhivi fizički ispred one koja se ažurira, potom se datoteka komprimuje i zapisuje u privremeni fajl, sledi kopiranje preostalih datoteka koje su u arhivi fizički iza one koja se ažurira u privremeni fajl i zapisivanje novog centralnog direktorijuma. Konačno, originalna arhiva se zamenjuje privremenim fajlom. Kako napreduje?Pošto kompresija može da bude dugotrajni proces, bilo bi vrlo nepraktično da se ona obavlja u jednom prolazu, bez informacija o tekućem statusu ili mogućnosti da se prekine od strane korisnika. Zbog toga se mora obezbediti komunikacija između CZipArchive objekta i ”spoljnog sveta”, najčešće nekog drugog objekta (npr. dijaloga). Ova komunikacija je dvosmerna: prilikom bilo koje dugotrajne operacije CZipArchive objekat u svakom prolazu šalje informaciju o trenutnom statusu, očekujući kao povratnu vrednost signal da li treba da nastavi sa radom ili ne. Windows nudi više načina da se ova komunikacija između objekata ostvari (putem slanja poruka, na primer), ali je u duhu C++-a ”najčistije” rešenje upotreba apstraktnog interfejsa. Svaki objekat koji želi da bude pozivan od strane CZipArchive objekta treba da nasledi apstraktnu klasu CZipArchiveTarget i implementira jednu jedinu funkciju – OnArchiveProgress(). Sa svoje strane, CZipArchive objekat ima pointer na CZipArchiveTarget objekat sa kojim komunicira – ako taj pointer nije NULL, pozivaće njegovu OnArchiveProgress() funkciju. Dodatni aspekt komunikacije predstavlja i reagovanje na greške koje mogu nastati prilikom rada sa arhivom. U trenutnoj implementaciji, CZipArchive objekat za bilo koji problem šalje CUserException, pa program koji namerava da reaguje na greške treba da uokviri pozive funkcija odgovarajućim try/catch blokom. Kvalitetna implementacija bi trebalo da uvede sopstvene izuzetke za svaki neregularni događaj i da ih razlikuje u programu. UpotrebaUpotreba CZipArchive objekta u programu je sasvim jednostavna – pogledajte primer prikazan na slici 1. Implementacija je, iako vrlo jednostavna, sasvim dovoljna za većinu praktičnih potreba. Čak i ako se pokaže da nije tako, izvorni kod klasa i prateći demo program možete pronaći na SezamuPro, u konferenciji PCPress ili na našoj Web strani www.pcpress.co.yu i prilagoditi potrebama. Zlib biblioteku ćete pronaći na adresi http://www.cdrom.com/pub/infoZip/zlib , a kompletnu specifikaciju Zip formata na http://www.pkware.com . Slika 1 #include ”ZipArchive.h” ... CZipArchive zipArchive1( ”MojaNovaArhiva.zip”, CFile::modeCreate ); ZipArchive1.AddFile( ”MojaDatoteka.cpp” ); CZipArchive zipArchive2( ”PostojecaArhiva.zip”, CFile::modeReadWrite | CFile::shareDenyWrite ); for ( int i = 0; i zipArchive2.GetZippedFileCount(); i++ ) { const CZipFile& zipFile = zipArchive2.GetZippedFile( i ); zipArchive2.ExtractFile( i ); // zipArchive2.ExtractFile( zipFile.GetFilename() ); } Prilozi:
pc045zip.zip (60,02 kB) |
|