Difference between revisions of "paszlib/ru"

From Free Pascal wiki
Jump to navigationJump to search
(Created page with "{{paszlib}} '''paszlib''' представляет собой преобразование стандартной библиотеки zlib на Паскаль: вам не...")
 
 
(8 intermediate revisions by the same user not shown)
Line 9: Line 9:
 
=TZipper=
 
=TZipper=
  
TZipper implements support for compressing and decompressing .zip files, but does not support all zip compression methods.
+
TZipper поддерживает сжатие и распаковку файлов .zip, но не поддерживает все методы сжатия zip.
  
==Documentation==
+
==Документация==
See [http://www.freepascal.org/docs-html/fcl/zipper/index.html official FPC documentation for Zipper]
+
См. [http://www.freepascal.org/docs-html/fcl/zipper/index.html официальную документацию FPC для Zipper]
  
==Examples==
+
==Примеры==
  
===Zip files===
+
===Упаковка файлов===
  
Create a zip file named as first parameter. Treats all other parameters as filenames to add, so you can specify e.g.
+
Создание zip-файла с именем в качестве первого параметра. Все остальные параметры обрабатываются как имена файлов для добавления, поэтому вы можете указать, например,  
  
 
<syntaxhighlight lang="dos">zipper newzip.zip autoexec.bat config.sys</syntaxhighlight>
 
<syntaxhighlight lang="dos">zipper newzip.zip autoexec.bat config.sys</syntaxhighlight>
Line 31: Line 31:
 
   OurZipper := TZipper.Create;
 
   OurZipper := TZipper.Create;
 
   try
 
   try
     // Define the file name of the zip file to be created
+
     // Опредяем имя создаваемого zip-файла.
 
     OurZipper.FileName := ParamStr(1);
 
     OurZipper.FileName := ParamStr(1);
 
     for I := 2 to ParamCount do
 
     for I := 2 to ParamCount do
       // Specify the names of the files to be included in the zip as first argument
+
       // В качестве первого аргумента указываем имена файлов, которые будут включены в zip-архив.
       // The second argument is the name of the file as it appears in the zip and
+
       // Второй аргумент - это имя файла в том виде, в котором оно отображается в zip-архиве, и
       // later in the file system after unzipping
+
       // позже в файловой системе после распаковки
 
       OurZipper.Entries.AddFileEntry(ParamStr(I), ParamStr(I));
 
       OurZipper.Entries.AddFileEntry(ParamStr(I), ParamStr(I));
     // Execute the zipping operation and write the zip file.
+
     // Выполняем операцию архивирования и записываем архивный файл.
 
     OurZipper.ZipAllFiles;
 
     OurZipper.ZipAllFiles;
 
   finally
 
   finally
Line 46: Line 46:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Please note that the names of the files to be zipped are assumed to contain only characters of the old IBM PC character set (code page 437). For extended characters (UTF-8), see the section below.
+
Обратите внимание, что имена архивируемых файлов должны содержать только символы из старого набора символов IBM PC (кодовая страница 437). Расширенные символы (UTF-8) см. в разделе ниже.
  
===Unzip files===
+
===Распаковка файлов===
  
Unzip all files in archive c:\test.zip into directory c:\windows\temp\
+
Распаковываем все файлы из архива c:\test.zip в папку c:\windows\temp\
  
 
<syntaxhighlight lang=pascal>uses
 
<syntaxhighlight lang=pascal>uses
Line 68: Line 68:
 
end.</syntaxhighlight>
 
end.</syntaxhighlight>
  
===Zip files with encoding filenames===
+
=== Упаковка файлов с сохранением кодировки имен файлов===
  
As already noted, the zip file format originally was written to support only code page 437 of the IBM PC. In modern operating systems, however, the encoding of file names is much more general.  
+
Как уже отмечалось, формат файла zip изначально был написан для поддержки только кодовой страницы 437 IBM PC. Однако в современных операционных системах кодировка имен файлов является гораздо более общей.
  
If your '''FPC is version 3.2 or newer''' you can take advantage of the boolean property <tt>UseLanguageEncoding</tt>. When set to <tt>true</tt>, file names are assumed to have the default encoding of FPC, in case of Lazarus programs this is UTF-8. Here any file name can be passed to the zipper (except for restrictions of the OS, of course).
+
Если у вас  версия '''FPC 3.2 или новее''', вы можете воспользоваться логическим свойством <tt>UseLanguageEncoding</tt>. Если установлено значение <tt>true</tt>, предполагается, что имена файлов имеют кодировку по умолчанию FPC, в случае программ Lazarus это UTF-8. Здесь в zipper можно передать любое имя файла (за исключением, конечно, ограничений ОС).
  
In the following practical routine for FPC 3.2, a zip file is created from the files in a given directory so that file names are considered correctly:
+
В следующей практической программе для FPC 3.2 из файлов в заданном каталоге создается zip-файл, чтобы имена файлов считались правильными:
 
<syntaxhighlight lang=pascal>
 
<syntaxhighlight lang=pascal>
 
uses
 
uses
Line 93: Line 93:
 
     OurZipper := TZipper.Create;
 
     OurZipper := TZipper.Create;
 
     try
 
     try
       // Set the name of the zip file to be created
+
       // Задаем имя создаваемого zip-файла
 
       OurZipper.FileName := AZipFileName;
 
       OurZipper.FileName := AZipFileName;
  
       // Read names of the files contained in ADirectory to a stringlist
+
       // Считываем имена файлов, содержащихся в ADirectory, в stringlist
 
       list := TStringList.Create;
 
       list := TStringList.Create;
 
       try
 
       try
         // FindAllFiles adds all file names matching the mask (e.g. '*.*')
+
         // FindAllFiles добавляет все имена файлов, соответствующие маске (например, '*.*'),
         // found in the given directory to the provided list.
+
         // найденных в указанном каталоге в предоставленный список.
         // When IncludingSubDirs is true the search continues recursively in
+
         // Если IncludingSubDirs - true, поиск продолжается рекурсивно и в
         // the subdirectories.
+
         // подкаталогах.
 
         FindAllFiles(list, ADirectory, AMask, IncludingSubDirs);
 
         FindAllFiles(list, ADirectory, AMask, IncludingSubDirs);
 
         for i := 0 to list.Count - 1 do
 
         for i := 0 to list.Count - 1 do
 
         begin
 
         begin
           // diskfilename is the name of the file to be zipped on the disk
+
           // diskfilename - это имя файла, который будет заархивирован на диске
 
           diskFileName := list[i];
 
           diskFileName := list[i];
           // archivefilename is the name of the file to be zipped as it appears
+
           // archivefilename - это имя файла, который нужно заархивировать, как он выглядит
           // in the zip. We remove the deirectory from the
+
           // в zip. Убираем директорию из
 
           archiveFileName := StringReplace(diskFileName, ADirectory, '', []);
 
           archiveFileName := StringReplace(diskFileName, ADirectory, '', []);
           // Store these filenames for the zipper
+
           // Сохраняем эти файлы для архиватора
 
           OurZipper.Entries.AddFileEntry(diskFileName, archiveFileName);
 
           OurZipper.Entries.AddFileEntry(diskFileName, archiveFileName);
 
         end;
 
         end;
Line 117: Line 117:
 
         list.Free;
 
         list.Free;
 
       end;
 
       end;
       // By default zipper writes file names in encoding of the IBM PC, CP437.
+
       // По умолчанию архиватор записывает имена файлов в кодировке IBM PC CP437.
       // UTF8 encoding is written when UseLanguageEncoding is true.
+
       // Кодировка UTF8 записывается, когда UseLanguageEncoding - true.
       OurZipper.UseLanguageEncoding := true;  // Requires FPC 3.2+
+
       OurZipper.UseLanguageEncoding := true;  // Требуется FPC 3.2+
       // create and write the zip file
+
       // создаем и записываем zip-файл
 
       OurZipper.ZipAllFiles;
 
       OurZipper.ZipAllFiles;
 
       Result := true;
 
       Result := true;
Line 130: Line 130:
 
end;</syntaxhighlight>
 
end;</syntaxhighlight>
  
If your '''FPC is older than v3.2''' you must convert the ArchiveFileName argument to CP437 - of course, this is not possible for all characters, and thus you must be very careful with zipper in this case. Note also that the <tt>DiskFileName</tt> must have the encoding required by the operating system, otherwise the file will not be found for zipping; in case of FPC before 3.0 this may require another code page conversion.
+
Если у вас версия '''FPC меньше v3.2''', вы должны преобразовать аргумент ArchiveFileName в CP437 - конечно, это возможно не для всех символов, и поэтому вы должны быть очень осторожны с архиватором в этом случае. Также обратите внимание, что <tt>DiskFileName</tt> должен иметь кодировку, требуемую операционной системой, иначе файл для архивирования не будет найден; в случае FPC до 3.0 может потребоваться еще одно преобразование кодовой страницы.
  
Here is the adaption of above example for an FPC version before 3.2:
+
Вот адаптация приведенного выше примера для версии FPC до 3.2:
 
<syntaxhighlight lang=pascal>
 
<syntaxhighlight lang=pascal>
 
uses
 
uses
Line 155: Line 155:
 
       list := TStringList.Create;
 
       list := TStringList.Create;
 
       try
 
       try
         // List all files in ADirectory and, if required, its sub-directories.
+
         // Перечислияем все файлы в ADirectory и, если необходимо, его подкаталоги.
 
         FindAllFiles(list, ADirectory, AMask, IncludingSubDirs);
 
         FindAllFiles(list, ADirectory, AMask, IncludingSubDirs);
 
         for i := 0 to list.Count-1 do
 
         for i := 0 to list.Count-1 do
 
         begin
 
         begin
           // Name of the file to be zipped on disk
+
           // Имя файла, который нужно заархивировать на диске
 
           diskFileName := list[i];
 
           diskFileName := list[i];
 
           {$IF FPC_FullVersion < 30000}
 
           {$IF FPC_FullVersion < 30000}
 
           diskFileName := UTF8ToWinCP(diskFileName);
 
           diskFileName := UTF8ToWinCP(diskFileName);
 
           {$IFEND}
 
           {$IFEND}
           // Name of the file to be zipped in the archive: we remove the
+
           // Имя файла, который нужно заархивировать в архиве: убираем
           // common path and thus make the files relative to the directory
+
           // общий путь и таким образом обозначаем файлы относительно каталога
           // into which they will be unzipped later.
+
           // в который они будут распакованы позже.
 
           archiveFileName := StringReplace(list[i], ADirectory, '', []);
 
           archiveFileName := StringReplace(list[i], ADirectory, '', []);
 
           archiveFileName := UTF8ToCP437(archiveFileName);
 
           archiveFileName := UTF8ToCP437(archiveFileName);
Line 174: Line 174:
 
         list.Free;
 
         list.Free;
 
       end;
 
       end;
       // Execute zipping action and create zip file
+
       // Выполняем действие архивирования и создаем архивный файл.
 
       OurZipper.ZipAllFiles;
 
       OurZipper.ZipAllFiles;
 
       Result := true;
 
       Result := true;
Line 183: Line 183:
 
end;</syntaxhighlight>
 
end;</syntaxhighlight>
  
Another restriction is the path delimiter in the archived file name. Specification of the zip format requires that forward slashes ('<tt>/</tt>') are used even in case of Windows. This is important for the one-parameter overload of <tt>TZipper.Entries.AddFileEntry(DiskfileName)</tt> which simply assume the archived file name to be equal to the <tt>DiskFileName</tt>, without any adaptions. Therefore, such a zip file will contain backslashes on Windows. It will unzip correctly on Windows, but not on Linux where the backslash is considered to be a valid file name character. It is highly recommended to use the two-parameter version of <tt>TZipper.Entries.AFileEntry(DiskFileName, ArchiveFileName)</tt> with <tt>ArchivefileName = DiskFileName</tt> because this procedure automatically replaces the path delimiters as required.
+
Еще одно ограничение - это разделитель пути в имени заархивированного файла. Спецификация формата zip требует, чтобы косая черта (слэш) ('<tt>/</tt>') использовалась даже в случае Windows. Это важно для однопараметрической перегрузки <tt>TZipper.Entries.AddFileEntry(DiskfileName)</tt>, которая просто предполагает, что имя заархивированного файла равно <tt>DiskFileName</tt>, без каких-либо адаптаций. Следовательно, такой zip-файл будет содержать обратный слэш в Windows. Он будет правильно распакован в Windows, но не в Linux, где обратный слэш считается допустимым символом имени файла. Настоятельно рекомендуется использовать двухпараметрическую версию <tt>TZipper.Entries.AFileEntry(DiskFileName, ArchiveFileName)</tt> с <tt>ArchivefileName = DiskFileName</tt>, поскольку эта процедура автоматически заменяет разделители путей по мере необходимости.
  
===Unzip files with encoding filenames===
+
===Распаковка файлов с сохранением кодировки имен===
  
 
<syntaxhighlight lang=pascal>uses
 
<syntaxhighlight lang=pascal>uses
Line 263: Line 263:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
More examples can be found in the FPC source directory:
+
Дополнительные примеры можно найти в исходном каталоге FPC:
* examples: [http://svn.freepascal.org/svn/fpc/trunk/packages/paszlib/examples/]  
+
* примеры: [http://svn.freepascal.org/svn/fpc/trunk/packages/paszlib/examples/]  
* the test program: [http://svn.freepascal.org/svn/fpc/trunk/packages/paszlib/tests]
+
* тестовые программы: [http://svn.freepascal.org/svn/fpc/trunk/packages/paszlib/tests]
  
===Unzip file to a stream===
+
===Распаковка файла в поток (память)===
  
In Lazarus, drop a TMemo, TButton, TEdit and TFileNameEdit on a form. Clicking the button will read the zip file in FileNameEdit, extract the file specified in the Edit box, and display the content in Memo.
+
В Lazarus поместите в форму TMemo, TButton, TEdit и TFileNameEdit. Нажатие кнопки приведет к чтению zip-файла в FileNameEdit, извлечению файла, указанному в поле Edit, и отображению содержимого в Memo.
  
 
<syntaxhighlight lang=pascal>
 
<syntaxhighlight lang=pascal>
Line 316: Line 316:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
===Zipping a whole directory tree===
+
===Архивирование всего дерева каталогов===
  
*This will recursively add the contents of 'C:\MyFolder' to the 'myzipfile.zip'
+
* Этот пример рекурсивно добавит содержимое 'C: MyFolder' в 'myzipfile.zip'
**Note that the absolute path is stored in the zipfile
+
** Обратите внимание, что в zip-файле абсолютный путь сохраняется.
**Note that this requires the Lazarus fileutil unit (which you can probably work around)
+
** Обратите внимание, что для этого требуется модуль Lazarus fileutil (который, вероятно, можно обойти)
  
 
<syntaxhighlight lang=pascal>
 
<syntaxhighlight lang=pascal>
Line 343: Line 343:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
===Zipping a whole directory tree storing only a relative path===
+
===Архивирование всего дерева каталогов с сохранением только относительного пути===
  
*This is more complicated, but it can be done
+
* Это сложнее, но это можно сделать
**Note that this requires the Lazarus fileutil unit (which you can probably work around)
+
** Обратите внимание, что для этого требуется модуль Lazarus fileutil (который, вероятно, можно обойти)
  
 
<syntaxhighlight lang=pascal>
 
<syntaxhighlight lang=pascal>
Line 365: Line 365:
 
       AZipper.Clear;
 
       AZipper.Clear;
 
       ZEntries := TZipFileEntries.Create(TZipFileEntry);
 
       ZEntries := TZipFileEntries.Create(TZipFileEntry);
       // Verify valid directory
+
       // Проверяем существование каталога
 
       If DirPathExists(RelativeDirectory) then
 
       If DirPathExists(RelativeDirectory) then
 
       begin
 
       begin
         // Construct the path to the directory BELOW RelativeDirectory
+
         // Создаем путь к каталогу НИЖЕ RelativeDirectory.
         // If user specifies 'C:\MyFolder\Subfolder' it returns 'C:\MyFolder\'
+
         // Если пользователь указал 'C:\MyFolder\Subfolder' это вернет 'C:\MyFolder\'
         // If user specifies 'C:\MyFolder' it returns 'C:\'
+
         // Если пользователь указал 'C:\MyFolder' это вернет 'C:\'
         // If user specifies 'C:\' it returns 'C:\'
+
         // Если пользователь указал 'C:\' это вернет 'C:\'
 
         i:=RPos(PathDelim,ChompPathDelim(RelativeDirectory));
 
         i:=RPos(PathDelim,ChompPathDelim(RelativeDirectory));
 
         szPathEntry:=LeftStr(RelativeDirectory,i);
 
         szPathEntry:=LeftStr(RelativeDirectory,i);
  
         // Use the FileUtils.FindAllFiles function to get everything (files and folders) recursively
+
         // Используйте функцию FileUtils.FindAllFiles для рекурсивного получения всего (файлов и папок)
 
         TheFileList:=TstringList.Create;
 
         TheFileList:=TstringList.Create;
 
         try
 
         try
Line 381: Line 381:
 
           for i:=0 to TheFileList.Count -1 do
 
           for i:=0 to TheFileList.Count -1 do
 
           begin
 
           begin
             // Make sure the RelativeDirectory files are not in the root of the ZipFile
+
             // Убедитесь, что файлы RelativeDirectory не находятся в корне ZipFile.
 
             ZEntries.AddFileEntry(TheFileList[i],CreateRelativePath(TheFileList[i],szPathEntry));
 
             ZEntries.AddFileEntry(TheFileList[i],CreateRelativePath(TheFileList[i],szPathEntry));
 
           end;
 
           end;
Line 392: Line 392:
 
       except
 
       except
 
         On E: EZipError do
 
         On E: EZipError do
           E.CreateFmt('Zipfile could not be created%sReason: %s', [LineEnding, E.Message])
+
           E.CreateFmt('Zip-файл %s не может быть создан по причине %s:', [LineEnding, E.Message])
 
       end;
 
       end;
 
     result := True;
 
     result := True;
Line 402: Line 402:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Note that this example uses an overloaded version of addfileentry() (compared to simple examples). This version allows you to specify the directory structure inside the Zip file, and, then, of course, the directory structure when its all unzipped. You can, for example, specify only the file name with no directory structure and have all files returned in one flat output directory. Even if they came for all over the place !
+
Обратите внимание, что в этом примере используется перегруженная версия addfileentry() (по сравнению с простыми примерами). Эта версия позволяет вам указать структуру каталогов внутри Zip-файла, а затем, конечно же, структуру каталогов после его распаковки. Вы можете, например, указать только имя файла без структуры каталогов, и все файлы будут возвращены в одном плоском выходном каталоге. Даже если они собраны отовсюду!
  
 
<syntaxhighlight lang=pascal>ZEntries.AddFileEntry(FullDiskPathToFile, FileName);</syntaxhighlight>
 
<syntaxhighlight lang=pascal>ZEntries.AddFileEntry(FullDiskPathToFile, FileName);</syntaxhighlight>
  
== See also ==
+
== См. также ==
  
* [http://www.freepascal.org/docs-html/fcl/zipper/index.html official FPC documentation for Zipper]
+
* [http://www.freepascal.org/docs-html/fcl/zipper/index.html официальная документация FPC для Zipper]
* [http://www.freepascal.org/~michael/articles/archives/archives.pdf Article] demonstrating handling tar, bzip2, gzip, zip files and Blowfish encryption in FreePascal/Lazarus. A good introduction even though it was written some time ago (a lot of functionality has been improved).
+
* [http://www.freepascal.org/~michael/articles/archives/archives.pdf Статья] демонстрация обработки файлов tar, bzip2, gzip, zip и шифрования Blowfish в FreePascal / Lazarus. Хорошее введение, хотя оно было написано некоторое время назад (улучшена большая часть функциональности).
 
* [[unzip|unzip]]
 
* [[unzip|unzip]]
* [[FreePascalArchivePackage]] Abbrevia archive/zip library
+
* [[FreePascalArchivePackage]] архиватор/zip-библиотека Abbrevia
 
* [http://code.google.com/p/kstools/ MIT licensed Delphi/Object Pascal library] that includes zip file support.
 
* [http://code.google.com/p/kstools/ MIT licensed Delphi/Object Pascal library] that includes zip file support.
  
Go back to [[Package_List|Packages List]]
+
Назад к [[Package_List|Packages List]]
  
 
[[Category:Tutorials]]
 
[[Category:Tutorials]]

Latest revision as of 16:14, 8 June 2021

Deutsch (de) English (en) 한국어 (ko) polski (pl) русский (ru)

paszlib представляет собой преобразование стандартной библиотеки zlib на Паскаль: вам не нужны никакие внешние зависимости. Его реализовал Jacques Nomssi Nzali (его старая домашняя страница мертва, см. продолжение проекта здесь). Он используется в FCL для реализации класса TCompressionStream.

Этот класс позволяет сжимать и распаковывать файлы .zip.

Основной модуль этого пакета - paszlib. Существуют и другие вспомогательные модули, но единственный модуль, который необходимо включить в типичную программу, - этот.

TZipper

TZipper поддерживает сжатие и распаковку файлов .zip, но не поддерживает все методы сжатия zip.

Документация

См. официальную документацию FPC для Zipper

Примеры

Упаковка файлов

Создание zip-файла с именем в качестве первого параметра. Все остальные параметры обрабатываются как имена файлов для добавления, поэтому вы можете указать, например,

zipper newzip.zip autoexec.bat config.sys
uses
  Zipper;
var
  OurZipper: TZipper;
  I: Integer;
begin
  OurZipper := TZipper.Create;
  try
    // Опредяем имя создаваемого zip-файла.
    OurZipper.FileName := ParamStr(1);
    for I := 2 to ParamCount do
      // В качестве первого аргумента указываем имена файлов, которые будут включены в zip-архив.
      // Второй аргумент - это имя файла в том виде, в котором оно отображается в zip-архиве, и
      // позже в файловой системе после распаковки
      OurZipper.Entries.AddFileEntry(ParamStr(I), ParamStr(I));
    // Выполняем операцию архивирования и записываем архивный файл.
    OurZipper.ZipAllFiles;
  finally
    OurZipper.Free;
  end;
end.

Обратите внимание, что имена архивируемых файлов должны содержать только символы из старого набора символов IBM PC (кодовая страница 437). Расширенные символы (UTF-8) см. в разделе ниже.

Распаковка файлов

Распаковываем все файлы из архива c:\test.zip в папку c:\windows\temp\

uses
  Zipper;
var
  UnZipper: TUnZipper;
begin
  UnZipper := TUnZipper.Create;
  try    
    UnZipper.FileName := 'c:\test.zip';
    UnZipper.OutputPath := 'c:\windows\temp';
    UnZipper.Examine;
    UnZipper.UnZipAllFiles;
  finally
    UnZipper.Free;
  end;
end.

Упаковка файлов с сохранением кодировки имен файлов

Как уже отмечалось, формат файла zip изначально был написан для поддержки только кодовой страницы 437 IBM PC. Однако в современных операционных системах кодировка имен файлов является гораздо более общей.

Если у вас версия FPC 3.2 или новее, вы можете воспользоваться логическим свойством UseLanguageEncoding. Если установлено значение true, предполагается, что имена файлов имеют кодировку по умолчанию FPC, в случае программ Lazarus это UTF-8. Здесь в zipper можно передать любое имя файла (за исключением, конечно, ограничений ОС).

В следующей практической программе для FPC 3.2 из файлов в заданном каталоге создается zip-файл, чтобы имена файлов считались правильными:

uses
  FileUtil, zipper;

function PackFiles(AZipFilename, ADirectory, AMask: String;
  IncludingSubDirs: Boolean): Boolean;
var
  OurZipper: TZipper;
  list: TStringList;
  i: Integer;
  diskFileName, archiveFileName: String;
begin
  Result := false;
  ADirectory := IncludeTrailingPathDelimiter(ADirectory);
  if DirectoryExists(ADirectory) then
  begin
     OurZipper := TZipper.Create;
     try
       // Задаем имя создаваемого zip-файла
       OurZipper.FileName := AZipFileName;

       // Считываем имена файлов, содержащихся в ADirectory, в stringlist
       list := TStringList.Create;
       try
         // FindAllFiles добавляет все имена файлов, соответствующие маске (например, '*.*'),
         // найденных в указанном каталоге в предоставленный список.
         // Если IncludingSubDirs - true, поиск продолжается рекурсивно и в
         // подкаталогах.
         FindAllFiles(list, ADirectory, AMask, IncludingSubDirs);
         for i := 0 to list.Count - 1 do
         begin
           // diskfilename - это имя файла, который будет заархивирован на диске
           diskFileName := list[i];
           // archivefilename - это имя файла, который нужно заархивировать, как он выглядит
           // в zip. Убираем директорию из
           archiveFileName := StringReplace(diskFileName, ADirectory, '', []);
           // Сохраняем эти файлы для архиватора
           OurZipper.Entries.AddFileEntry(diskFileName, archiveFileName);
         end;
       finally
         list.Free;
       end;
       // По умолчанию архиватор записывает имена файлов в кодировке IBM PC CP437.
       // Кодировка UTF8 записывается, когда UseLanguageEncoding - true.
       OurZipper.UseLanguageEncoding := true;  // Требуется FPC 3.2+
       // создаем и записываем zip-файл
       OurZipper.ZipAllFiles;
       Result := true;
     finally
       OurZipper.Free;
     end;
  end else
    Result := false;
end;

Если у вас версия FPC меньше v3.2, вы должны преобразовать аргумент ArchiveFileName в CP437 - конечно, это возможно не для всех символов, и поэтому вы должны быть очень осторожны с архиватором в этом случае. Также обратите внимание, что DiskFileName должен иметь кодировку, требуемую операционной системой, иначе файл для архивирования не будет найден; в случае FPC до 3.0 может потребоваться еще одно преобразование кодовой страницы.

Вот адаптация приведенного выше примера для версии FPC до 3.2:

uses
  FileUtil, LConvEncoding, LazUTF8, Zipper;

function PackFiles(AZipFilename, ADirectory, AMask: String;
  IncludingSubdirs: Boolean): Boolean;
var
  OurZipper: TZipper;
  list: TStringList;
  i: Integer;
  diskFileName, archiveFileName: String;
begin
  Result := false;
  if AMask = '' then AMask := '*.*';
  ADirectory := IncludeTrailingPathDelimiter(ADirectory);
  if DirectoryExists(ADirectory) then
  begin
     OurZipper := TZipper.Create;
     try
       OurZipper.FileName := AZipFileName;
       list := TStringList.Create;
       try
         // Перечислияем все файлы в ADirectory и, если необходимо, его подкаталоги.
         FindAllFiles(list, ADirectory, AMask, IncludingSubDirs);
         for i := 0 to list.Count-1 do
         begin
           // Имя файла, который нужно заархивировать на диске
           diskFileName := list[i];
           {$IF FPC_FullVersion < 30000}
           diskFileName := UTF8ToWinCP(diskFileName);
           {$IFEND}
           // Имя файла, который нужно заархивировать в архиве: убираем
           // общий путь и таким образом обозначаем файлы относительно каталога
           // в который они будут распакованы позже.
           archiveFileName := StringReplace(list[i], ADirectory, '', []);
           archiveFileName := UTF8ToCP437(archiveFileName);
           OurZipper.Entries.AddFileEntry(diskFileName, archiveFileName);
         end;
       finally
         list.Free;
       end;
       // Выполняем действие архивирования и создаем архивный файл.
       OurZipper.ZipAllFiles;
       Result := true;
     finally
       OurZipper.Free;
     end;
  end;
end;

Еще одно ограничение - это разделитель пути в имени заархивированного файла. Спецификация формата zip требует, чтобы косая черта (слэш) ('/') использовалась даже в случае Windows. Это важно для однопараметрической перегрузки TZipper.Entries.AddFileEntry(DiskfileName), которая просто предполагает, что имя заархивированного файла равно DiskFileName, без каких-либо адаптаций. Следовательно, такой zip-файл будет содержать обратный слэш в Windows. Он будет правильно распакован в Windows, но не в Linux, где обратный слэш считается допустимым символом имени файла. Настоятельно рекомендуется использовать двухпараметрическую версию TZipper.Entries.AFileEntry(DiskFileName, ArchiveFileName) с ArchivefileName = DiskFileName, поскольку эта процедура автоматически заменяет разделители путей по мере необходимости.

Распаковка файлов с сохранением кодировки имен

uses
  Zipper, LConvEncoding;
...
function EndPathCP866ToUTF8(AText:string):string;
var
  c,i:integer;
  s,s1,s2,chr:string;
begin
  s:='';
  c:=UTF8Length(AText);
  for i:=c downto 1 do
  begin
       chr:=UTF8Copy(AText,i,1);
       if ((not(chr='/')) and (not(chr='\')))or(i=c) then
       begin
            s:=UTF8Copy(AText,i,1)+s;
       end
       else begin
            s:=UTF8Copy(AText,i,1)+s;
            break;
       end;
  end;
  dec(i);
  s1:=UTF8Copy(AText,1,i);
  s2:=CP866ToUTF8(s);
  Result:=s1+s2;
end;

function UnPackFiles(Filename, UnPackPath: String): Integer;
var
  UnZipper          :TUnZipper; //PasZLib
  UnPackFileDir,
  ADiskFileName,
  ANewDiskFileName,
  AArchiveFileName  :String;
  i                 :integer;
begin
  Result:=-1;
  if FileExists(Filename)and DirectoryExists(UnPackPath) then
  begin
       UnPackFileDir :=SysUtils.IncludeTrailingPathDelimiter(UnPackPath);
       UnZipper      :=TUnZipper.Create;
       try
          UnZipper.FileName   := Filename;
          UnZipper.OutputPath := UnPackPath;
          UnZipper.Examine;
          UnZipper.UnZipAllFiles;

          for i:=UnZipper.Entries.Count-1 downto 0 do
          begin
              AArchiveFileName:=UnZipper.Entries.Entries[i].ArchiveFileName;
              AArchiveFileName:=EndPathCP866ToUTF8(AArchiveFileName);
              AArchiveFileName:=UTF8ToSys(AArchiveFileName);
              ANewDiskFileName:=UnPackFileDir+AArchiveFileName;
              ADiskFileName   :=UnPackFileDir+UnZipper.Entries.Entries[i].DiskFileName;

              if FileExists(ADiskFileName) then
              begin
                 RenameFile(ADiskFileName, ANewDiskFileName);
              end
              else if DirectoryExists(ADiskFileName) then
              begin
                 ADiskFileName    :=SysUtils.IncludeTrailingPathDelimiter(ADiskFileName);
                 ANewDiskFileName :=SysUtils.IncludeTrailingPathDelimiter(ANewDiskFileName);
                 RenameFile(ADiskFileName, ANewDiskFileName);
              end;
          end;

          Result:=1;
       finally
          UnZipper.Free;
       end;
  end;
end;

Дополнительные примеры можно найти в исходном каталоге FPC:

  • примеры: [1]
  • тестовые программы: [2]

Распаковка файла в поток (память)

В Lazarus поместите в форму TMemo, TButton, TEdit и TFileNameEdit. Нажатие кнопки приведет к чтению zip-файла в FileNameEdit, извлечению файла, указанному в поле Edit, и отображению содержимого в Memo.

uses
  Zipper;

...

procedure TForm1.Button1Click(Sender: TObject);
begin
  ExtractFileFromZip(FileNameEdit1.FileName,Edit1.Text);
end;

procedure TForm1.DoCreateOutZipStream(Sender: TObject; var AStream: TStream;
  AItem: TFullZipFileEntry);
begin
  AStream:=TMemorystream.Create;
end;

procedure TForm1.DoDoneOutZipStream(Sender: TObject; var AStream: TStream;
  AItem: TFullZipFileEntry);
begin
  AStream.Position:=0;
  Memo1.lines.LoadFromStream(Astream);
  Astream.Free;
end;

procedure TForm1.ExtractFileFromZip(ZipName, FileName: string);
var
  ZipFile: TUnZipper;
  sl:TStringList;
begin
  sl:=TStringList.Create;
  sl.Add(FileName);
  ZipFile := TUnZipper.Create;
  try
    ZipFile.FileName := ZipName;
    ZipFile.OnCreateStream := @DoCreateOutZipStream;
    ZipFile.OnDoneStream:=@DoDoneOutZipStream;
    ZipFile.UnZipFiles(sl);
  finally
    ZipFile.Free;
    sl.Free;
  end;
end;

Архивирование всего дерева каталогов

  • Этот пример рекурсивно добавит содержимое 'C: MyFolder' в 'myzipfile.zip'
    • Обратите внимание, что в zip-файле абсолютный путь сохраняется.
    • Обратите внимание, что для этого требуется модуль Lazarus fileutil (который, вероятно, можно обойти)
Uses ...Zipper,FileUtil
var
  AZipper: TZipper;
  TheFileList:TStringList;
begin
  MyDirectory:='C:\MyFolder';
  AZipper := TZipper.Create;
  AZipper.Filename := 'myzipfile.zip';
  TheFileList:=TStringList.Create;
  try
    FindAllFiles(TheFileList, MyDirectory);
    AZipper.Entries.AddFileEntries(TheFileList);
    AZipper.ZipAllFiles;
  finally
    TheFileList.Free;
    AZipper.Free;
  end;
end;

Архивирование всего дерева каталогов с сохранением только относительного пути

  • Это сложнее, но это можно сделать
    • Обратите внимание, что для этого требуется модуль Lazarus fileutil (который, вероятно, можно обойти)
Uses ...Zipper,FileUtil,strutils
var
  AZipper: TZipper;
  szPathEntry:String;
  i:Integer;
  ZEntries : TZipFileEntries;
  TheFileList:TStringList;
  RelativeDirectory:String;
begin
  AZipper := TZipper.Create;
  try
    try
      AZipper.Filename := 'myzipfile.zip';
      RelativeDirectory:='C:\MyFolder\MyFolder\';
      AZipper.Clear;
      ZEntries := TZipFileEntries.Create(TZipFileEntry);
      // Проверяем существование каталога
      If DirPathExists(RelativeDirectory) then
      begin
        // Создаем путь к каталогу НИЖЕ RelativeDirectory.
        // Если пользователь указал 'C:\MyFolder\Subfolder' это вернет 'C:\MyFolder\'
        // Если пользователь указал 'C:\MyFolder' это вернет 'C:\'
        // Если пользователь указал 'C:\' это вернет 'C:\'
        i:=RPos(PathDelim,ChompPathDelim(RelativeDirectory));
        szPathEntry:=LeftStr(RelativeDirectory,i);

        // Используйте функцию FileUtils.FindAllFiles для рекурсивного получения всего (файлов и папок)
        TheFileList:=TstringList.Create;
        try
          FindAllFiles(TheFileList, RelativeDirectory);
          for i:=0 to TheFileList.Count -1 do
          begin
            // Убедитесь, что файлы RelativeDirectory не находятся в корне ZipFile.
            ZEntries.AddFileEntry(TheFileList[i],CreateRelativePath(TheFileList[i],szPathEntry));
          end;
        finally
          TheFileList.Free;
        end;
      end;
      if (ZEntries.Count > 0) then
        AZipper.ZipFiles(ZEntries);
      except
        On E: EZipError do
          E.CreateFmt('Zip-файл %s не может быть создан по причине %s:', [LineEnding, E.Message])
      end;
    result := True;
  finally
    FreeAndNil(ZEntries);
    AZipper.Free;
  end;  
end;

Обратите внимание, что в этом примере используется перегруженная версия addfileentry() (по сравнению с простыми примерами). Эта версия позволяет вам указать структуру каталогов внутри Zip-файла, а затем, конечно же, структуру каталогов после его распаковки. Вы можете, например, указать только имя файла без структуры каталогов, и все файлы будут возвращены в одном плоском выходном каталоге. Даже если они собраны отовсюду!

ZEntries.AddFileEntry(FullDiskPathToFile, FileName);

См. также

Назад к Packages List