Delphi programming blog
Источник: http://teran.karelia.pro/articles/item_4491.html
 

XE2: Работа с ZIP архивами

Опубликовано 02.10.2011 г. 19:58

Вернувшись из отпуска из столицы Чехии - города Прага (если вы там еще не были, то обязательно должны там побывать), можно приступать к освоению нового релиза Delphi XE2. Пока что я не буду затрагивать такие новые технологии как LiveBinding & FireMonkey, а остановлюсь на более простом - новых возможностях RTL. В общем то их не так уж много, и думаю наиболее полезное новшество - поддержка Zip-архивов.

Если посмотреть на список усовершенствований RTL, то с точки зрения нового функционала тут только два класса - TZipFile & TLoginCredentialService. Т.е. я имею в виду, что лишь эти классы решают некоторые прикладные задачи, остальные еже новшества расширяют либо улучшают существующее. TOSVersion - возвращает информацию об операционной системе (опять же работы никакой не выполняет), новые методы для TPoint и т.п. я думаю будут весьма полезны. Сам недавно удивлялся почему их до сих пор не было, когда писал мыльные пузыри. Но вернемся к работе с zip-архивами. Упомянутый функционал расположен в модуле System.Zip. И тут можно заметить два косяка. Во-первых написав в uses System.Z в выпадающем списке будет только ZLib, а модуль Zip будет отсутствовать (странность). Второе, что можно заметить - открыв искомый модуль, вы увидите что весь код откомментирован с использованием XML тегов. Но вот незадача - кажется Help Insight не показывает документацию в scoped-модулях. Т.е если в обычном Unit1.pas при наведении на тип (класс)/метод и т.п, перед которым есть описание, во всплывающей подсказке это описание присутствует, то в scoped-модулях (модулях с пространствами имен) XML-описание игнорируется. Ну да и черт с ним. Еще один интересный факт в том, что сам код задокументирован, однако в документации (справке) описаний нет. Но опять же, вернемся к работе непосредственно с архивом. Для примера я набросал простенькое приложение - открытие архива, листинг файлов внутри с выводом коэффициента сжатия + разархивация выбранных файлов.
 
Для работы с zip-архивами используется класс TZipFile. Для работы с файлом можно использовать два подхода: либо вам нужно знать о внутренностях архива, либо вы просто хотите распаковать его целиком, без знания что там внутри и т.п. В первом случае, вам потребуется использовать методы Open/Close для открытия файла-архива при начале работы, и закрытия в конце. Рассмотрим такой первый вариант. Приведенный ниже пример кода открывает архив для чтения, заполняет ZipView: TListView именами файлов в архиве, для каждого файла извлекает его заголовок и определяет коэффициент сжатия по размеру сжатого и несжатого файла, также определяет зашифрован файл или нет. По поводу шифрованных архивов, то работа с ними по большому счету не поддерживается (насколько я понял). Т.е если ваш архив запакован с использованием пароля, то распаковать вы его не сможете. А для файлов входящих в архив вы можете извлечь заголовок, и определить зашифрован файл или нет. Нулевой бит поля flag структуры TZipHeader определяет шифрование, т.е если он установлен, то файл зашифрован.
procedure TMainForm.OpenButtonClick(Sender: TObject);
var i : integer;
    li : TListItem;
    exPath : string;
    zh : TZipHeader;
    ratio : real;
begin
//    if not OpenDialog.Execute() then exit;
//    FZipFileName := OpenDialog.FileName;

    FZipFileName := 'C:\Users\teran\Desktop\radstudio_xe2_win_esd.zip';

    exPath := ExtractFilePath(FZipFileName) + ExtractFileName(FZipFileName);
    exPath := ChangeFileExt(exPath,'') + '\';
    try
        //check archive is valid
        if not FZip.IsValid(FZipFileName) then begin
            FZipFileName := '';
            exPath := '';
            exit;
        end;

        //Open ZIP-Archive and get ZIP-archive comment
        FZip.Open(FZipFileName, zmRead);
        CommentLabel.Caption := Fzip.Comment;

        for i := 0 to FZip.FileCount - 1 do begin
            //get i-th file headers
            zh := fzip.FileInfo[i];

            // add row to ListView
            li := zipView.Items.Add();
            li.Caption := intToStr(i + 1);
            with li.SubItems do begin
                Add(FZip.FileName[i]);
                Add(Fzip.FileComment[i]);

                //calc compress-ratio
                if zh.UncompressedSize > 0 then
                    ratio := 1 - zh.CompressedSize / zh.UncompressedSize
                else ratio := 0;
                Add(Format('%.1f%%',[ratio*100]));

                //check encryption (0-bit in Flag)
                if 0 in TIntegerSet(integer(zh.Flag)) then   Add('yes')
                else Add('no');
            end;
        end;

        //close archive
        FZip.Close();
    finally
        FileEdit.Text := FZipFileName;
        PathEdit.text := exPath;
    end;
end;
В принципе такого функционала достаточно для простой поддержки zip-архивов в ваших приложениях. После того, как мы увидели содержимое архива, можем попробовать распаковать его. Как я уже говорил, в некоторых случаях открывать и закрывать архив не требуется. Так что если просто хотите полностью распаковать архив, можно использовать такой код:
    fzip := TZipFile.Create();
    try
        fzip.ExtractZipFile(FZipFileName, exPath);
    finally
        fzip.Free();
    end;
Но если как в нашем случае требуется извлечь только выбранные файлы, то необходимо вновь открыть файл архива.
procedure TMainForm.ExtractButtonClick(Sender: TObject);
var exPath : string;
    i : integer;
begin
    exPath := PathEdit.Text;
    FZip.Open(FZipFileName, zmRead);
    try
        if not ForceDirectories(exPath) then exit;

        for i := 0 to FZip.FileCount - 1 do begin
            if ZipView.Items[i].Checked then
                FZip.Extract(i, exPath, true);
        end;
    finally
        FZip.Close();
    end;
end;
Замечу, что метод Extract имеет два перегруженных варианта: либо вы используете индекс файла (номер), либо имя. С распаковкой все ясно. Теперь о том, как создать архив, и добавить туда файлы. Чтобы заархивировать, к примеру, директорию с файлами, необходимо создать экземпляр TZipFile, открыть его для записи, и с помощью метода Add добавить туда все необходимые файлы. Ниже приведен фрагмент кода, который создает архив и добавляет в него все файлы, находящиеся в папке с проектом. При это файлы добавляются рекурсивно (т.е из вложенных директорий тоже), исполняемый файл не добавляется, и поскольку выходной архив располагается в папке с проектом, то он тоже исключается. Не знаю как там было в ХЕ, но стоит заметить что в ХЕ2 директория в которую складываются выходные файлы (ехе, dcu и т.п) имеет вид Platform/Build configuration, т.е в моем случае Win32/Debug. Конечно, очевидно, что платформы в ХЕ не было, но мб вариант сборки и был, чего мне иногда сказать не хватало в Delphi 2010 (хотя конечно это можно указать в настройках). К слову сказать, еще в C++ Builder'e 2007 output dir был Release или Debug, а в Delphi это пришло только сейчас (или в ХЕ)
procedure TMainForm.CompressButtonClick(Sender: TObject);
var z : TZipFile;
    path, f, zname, zfn : string;
    fa : TStringDynArray;
begin
    path := ExtractFileDir(Application.ExeName);

    z := TZipFile.Create();
    try
        zname := path + '\test.zip';

        if FileExists(zname) then DeleteFile(zname);
        z.Open(zname, zmWrite);

        path := TDirectory.GetParent(path);
        path := TDirectory.GetParent(path);

        fa := TDirectory.GetFiles(path,'*.*', TSearchOption.soAllDirectories );
        for f in fa do begin
            if (f = zname) or (f = Application.ExeName) then continue;
            zfn := copy(f, length(path) + 2, length(f) - length(path)+ 2);
            z.Add(f, zfn );
        end;

        z.Close();
    finally
        z.Free();
    end;
end;
Метод Add имеет несколько перегруженных версий. Самая простая - указывает только имя файла. Если мы воспользуемся ей, то получим архив с файлами, который не будет учитывать исходную структуру дерева папок. Второй не обязательный параметр как раз таки и предназначен, чтобы задавать имя, под которым файл будет занесен в архив, т.е. мы можем задать имена вложенных папок. Собранный подобным образом архив успешно распаковывается с помощью 7z. Вспомним о том, что шифрованные архивы не поддерживаются, поэтому если вы попробуете создать в стороннем архиваторе архив с паролем, то загрузив его в с помощью первого фрагмента кода, в столбце encrypted получим значение yes. После распаковки такого архива файлы будут нечитабельны. На самом деле немного странно не видеть в данном классе метода для добавления директории целиком - наподобие TZipFile.AddDirectory(path : string; recursive : boolean = true), но думаю в будущем и он появится, а пока что реализовать его руками, как показано выше не составляет труда. Отмечу весьма важный факт, что при вызове метода Add файл сразу начинает обрабатываться. Так что при добавлении большого файла программа перестанет откликаться. Так что вывод один - работу с файлом архива необходимо проводить в отдельном потоке.
Метки:  archive  |  zip 

Комментарии

z12
12.12.2012 в 15:34
Всё бы ничего вот только с русскими названиями файлов туго у TZipFile... =(
Arioch
28.01.2013 в 16:34
http://qc.embarcadero.com/wc/qcmain.aspx?d=112168
Олько
02.08.2013 в 15:17
А как же быть с запароленными архивами? подскажите что-нибудь..
teran
06.08.2013 в 17:14
а судя по всему - никак. Данный класс не поддерживает использование запароленных архивов. варианты тут могут быть разные:
1) использовать сторонние библиотеки классов, например, как пишут Jedi Code Library и их класс TJclZipCompressArchive
2) использовать сторонии библиотеки для работы с архивами (7z.dll), к примеру.
3) использовать системную dll для работы с zip-папками
95Colleen
29.01.2017 в 03:25
I must say you have high quality content here. Your page
can go viral. You need initial boost only. How to get it?
Search for; Etorofer's strategies
BridgettSmall
27.11.2017 в 20:59
I have checked your blog and i've found some duplicate content, that's why you don't rank
high in google's search results, but there is a tool that can help you to create 100% unique articles, search for:
Boorfe's tips unlimited content
feet video
11.12.2017 в 22:38
Interesting article, bookmarked
get more instagram followers
19.01.2018 в 00:44
Greetings I really enjoyed reading this article
Revenuehits
19.01.2018 в 14:25
This article is interesting !!!
LettieChief
13.03.2018 в 16:01
I have checked your blog and i've found some duplicate
content, that's why you don't rank high in google's search
results, but there is a tool that can help you to create
100% unique articles, search for: boorfe's tips unlimited content
- Имя
- e-mail*
- Сайт
вы можете использовать теги [i],[b],[code],[quote]
Дополнительно