Показаны сообщения с ярлыком SharpZipLib. Показать все сообщения
Показаны сообщения с ярлыком SharpZipLib. Показать все сообщения

понедельник, 21 июня 2010 г.

Архивирование, разархивирование потоков, файлов и папок с использованием SharpZipLib .net & .net compact

Инструментарий: Visual Studio 2008 PRO, C#, SharpZipLib 0.85.5.0 и классы из пространства имен SharpZipLib.Zip

Содержание статьи:
1. работа с потоками
2. Работа с потоками, файлы и папки
3. Проще простого или FastZip class
4. Адаптируем под .net compact
5. Заключение

Исходники к статье

Данная статья поможет разобраться с API SharpZipLib, но раскрывает его только поверхностно и использует классы из пространства имен ICSharpCode.SharpZipLib.Zip. Более полные данные вы можете найти в документации на официальном сайте.

Работа с потоками.

Создадим новое консольное приложение с названием SharpZipLibAndStream. Добавим ссылку на библиотеку SharpZipLib и добавим в код:

  1. using ICSharpCode.SharpZipLib.Zip;  
  2. using System.IO;  


Теперь создадим эталонный поток, с которым в последствии сверим сжатый поток.

  1. MemoryStream memoryStream = new MemoryStream();  


Заполним его данными с помощью метода FillStream

  1. FillStream(memoryStream,10);  


Код метода FillStream:

  1. private static void FillStream(Stream stream, int times)  
  2. {  
  3.   string temp = "The quick brown fox jumps over the lazy dog";  
  4.   string data = string.Empty;  
  5.   for (int i = 0; i < times; i++) data += temp;  
  6.   byte[] buffer = System.Text.Encoding.UTF8.GetBytes(data);  
  7.   stream.Write(buffer, 0, buffer.Length);  
  8. }  


Как видно, из кода в поток помещается десять копий одной строки

  1. Console.WriteLine(string.Format("Изначальная длинна потока: {0} bytes", memoryStream.Length));  


Создадим экземпляр класса ZipOutputStream, в своем конструкторе он принимает единственный аргумент – поток, в который он будет сжимать данные.

  1. MemoryStream zippedMemoryStream = new MemoryStream();  
  2. ZipOutputStream zipStream = new ZipOutputStream(zippedMemoryStream);  


Теперь нам необходимо ( в противном случае мы получим InvalidOperationException:No Open Entry, при попытке записи данных в поток) добавить запись в оглавление архива, представленное классом ZipEntry, один из конструкторов которого принимает строку, как имя.

  1. ZipEntry zipEntry = new ZipEntry("myData");  
  2. zipStream.PutNextEntry(zipEntry);  


Поток готов к заполнению данными, заполним его аналогично memoryStream.

  1. FillStream(zipStream,10);  


После чего закроем ZipEntry

  1. zipStream.CloseEntry();  
  2. Console.WriteLine(string.Format("Длинна потока в архиве: {0} bytes", zippedMemoryStream.Length));  


Теперь можно сравнить длинну потоков – memoryStream 430 bytes и zippedMemoryStream 104.
Если мы изменим значение times в FillStream на 0, то получим значения 0 и 56,
56 bytes – столько занимает текущая запись в заголовке архива. Далее поставим значение 1 - выходные данные изменились на 43 и 100, 100 – 56 = 44, чуть больше самой строки (строка “The quick brown fox jumps over the lazy dog” включает в себя все буквы латиницы и довольно сложна для сжатия), что показывает малую эффективность при работе с данными небольшого размера. Изменим значение на 100 и получим 4300 и 125 как видно из результатов, ZipOutputStream прекрасно справляется с дублированием.

Разархивируем сжатый поток zippedMemoryStream.

Для извлечения данных из заархивированного потока используется класс ZipInputStream, конструктор которого принимает заархивированный поток.

  1. ZipInputStream zipInputStream = new ZipInputStream(zippedMemoryStream);  


Так как мы уже работали с zippedMemoryStream, переставим курсор в начало.

  1. zippedMemoryStream.Position = 0;  


После этого создадим в памяти новый поток для извлечения результатов.

  1. MemoryStream fromZippedMemoryStream = new MemoryStream();  


Выберем первую запись из оглавления архива и заполним поток разархивированными данными.

  1. ZipEntry entry = zipInputStream.GetNextEntry();  
  2. byte[] outputBuffer = new byte[zipInputStream.Length];  
  3. zipInputStream.Read(outputBuffer, 0, outputBuffer.Length);  
  4. fromZippedMemoryStream.Write(outputBuffer, 0, outputBuffer.Length);  
  5. Console.WriteLine(string.Format("Длинна разархивированного потока: {0} bytes", fromZippedMemoryStream.Length));  



Работа с потоками, файлы и папки.


В данной части мы заархивируем два текстовых файла с помощью ZipOutputStream, один из которых будет лежать в корне архива, а другой будет находиться внутри нескольких вложенных папок. После чего произведем распаковку архива на диск с помощью ZipInputStream.
Первоначально добавим в наш solution новое консольное приложение, с именем SharpZipLibStreamFilesAndFolders и сделаем его активным. Добавим ссылку на библиотеку SharpZipLib и добавим в код:

  1. using ICSharpCode.SharpZipLib.Zip;  
  2. using System.IO;  


Далее вызовем метод

  1. CreateFilesAndDirectiries();   


Код метода:

  1. private static void CreateFilesAndDirectiries()  
  2. {  
  3.     List<filestream> files = new List<filestream>();  
  4.     files.Add(File.Create("file1.txt"));  
  5.     files.Add(File.Create("file2.txt"));  
  6.     files.ForEach(FillHelloWorldInStream);  
  7.     files.ForEach(fStream => fStream.Close());  
  8. }  
  9.   
  10. private static void FillHelloWorldInStream(FileStream file)  
  11. {  
  12.     byte[] buffer = Encoding.UTF8.GetBytes("Hello World");  
  13.     file.Write(buffer, 0, buffer.Length);  
  14. }  
  15. </filestream></filestream>  


Как видно из кода – мы создаем в папке приложения два файла с именами file1.txt и file2.txt, потом записываем в каждый из файлов строку “Hello World” и закрываем потоки.


Займемся архивированием.


Далее мы создадим в папке с приложением новый файл my.zip, и передадим его FileStream поток в конструктор класса ZipOutputStream.

  1. ZipOutputStream zipOutStream = new ZipOutputStream(File.Create("my.zip"));  


После чего вызовем метод CreateFileZipEntry.

  1. CreateFileZipEntry(zipOutStream, "file1.txt""file1.txt");  


Код метода:

  1. private static void CreateFileZipEntry(ZipOutputStream zipOutStream, string name, string filePath)  
  2. {  
  3.    ZipEntry fileZipEntry = new ZipEntry(name);  
  4.    zipOutStream.PutNextEntry(fileZipEntry);  
  5.    FileStream fileStram = File.Open(filePath, FileMode.Open);  
  6.    byte[] buffer = new byte[fileStram.Length];  
  7.    fileStram.Read(buffer, 0, buffer.Length);  
  8.    zipOutStream.Write(buffer, 0, buffer.Length);  
  9.    zipOutStream.CloseEntry();  
  10. }  


Данный метод принимает три аргумента: поток ZipOutputStream в который будет добавлен экземпляр ZipEntry и архивные данные, имя, которое будет присвоено файлу внутри архива, а так же путь к файлу, данные из которого будут заархивированы.


Первоначально метод создает новую запись ZipEntry с переданным в метод именем (имя влияет на название файла внутри архива и на его местонахождение), после чего добавляет ее к оглавлению архива. Далее извлекается FileStream из файла, имя которого так же было передано в метод, читаются все его данные и записываются в ZipOutputStream, который архивирует их. После чего закрывается текущая ZipEntry.

Далее повторим вызов метода для второго файла, но изменим имя.

  1. CreateFileZipEntry(zipOutStream, @"folder1\folder2\folder3\file2.txt""file2.txt");  


В результате этого вызова в архиве будет создана иерархия каталогов, в последнем из которых будет находиться file2.txt.

Все необходимые данные переданы в архив, после чего вызываем

  1. zipOutStream.Close();  



Теперь разархивируем только что созданный нами архив.


Создадим директорию в папке приложения, куда разархивируем данные.

  1. Directory.CreateDirectory("ZipOutPut");  


Создадим экземпляр класса ZipInputStream и передадим в его конструктор экземпляр FileStream, нашего архива.

  1. ZipInputStream zipInputStream = new ZipInputStream(File.Open("my.zip", FileMode.Open));  


Получим первую запись из заголовка архива.

  1. ZipEntry zipEntryFromZippedFile = zipInputStream.GetNextEntry();  


Разархивируем все в директорию ZipOutPut

  1. while (zipEntryFromZippedFile != null)  
  2. {  
  3.      if (zipEntryFromZippedFile.IsFile)  
  4.      {  
  5.          FileInfo fInfo = new FileInfo(string.Format("ZipOutPut\\{0}", zipEntryFromZippedFile.Name));  
  6.          if (!fInfo.Directory.Exists) fInfo.Directory.Create();                      
  7.   
  8.          FileStream file = fInfo.Create();  
  9.          byte[] bufferFromZip = new byte[zipInputStream.Length];  
  10.          zipInputStream.Read(bufferFromZip, 0, bufferFromZip.Length);  
  11.          file.Write(bufferFromZip, 0, bufferFromZip.Length);  
  12.          file.Close();                
  13.       }  
  14.       zipEntryFromZippedFile = zipInputStream.GetNextEntry();  
  15. }  
  16. zipInputStream.Close();  


Мы по очереди перебираем все записи в заголовке архива и, если запись относится к файлу, проверяем, существует ли директория, к которой относится файл, при необходимости создавая оную. После создания файла, с обозначенным именем, читаем данные из архива и записываем их в FileStream созданного файла.

Проще простого или FastZip class

Если необходимо быстро создать архив или разархивировать архивный файл, то самый простой и быстрый способ использовать FastZip class.

Добавим в наш solution новое консольное приложение, с именем SharpZipLibFastZip, и сделаем его активным. Добавим ссылку на библиотеку SharpZipLib и добавим в код:

  1. using ICSharpCode.SharpZipLib.Zip;  
  2. using System.IO;  


Теперь вызовем метод CreateFilesAndDirectiries(), рассмотренный нами ранее, но с небольшими изменениями.

  1. private static void CreateFilesAndDirectiries()  
  2. {  
  3.      Directory.CreateDirectory("ToZip");  
  4.      List<filestream> files = new List<filestream>();  
  5.      files.Add(File.Create("ToZip\\file1.txt"));  
  6.      files.Add(File.Create("ToZip\\file2.txt"));  
  7.      files.ForEach(FillHelloWorldInStream);  
  8.      files.ForEach(fStream => fStream.Close());  
  9. }  
  10. </filestream></filestream>  


В папке приложения создается директория с названием “ToZip” и уже в ней будут находиться два текстовых файла.

Далее все гораздо проще, чем в предыдущих разделах.

  1. FastZip zip = new FastZip();  
  2. zip.CreateZip("my.zip","ToZip"true,null);  


Вот и все ;)

В папке приложения будет создан архив с именем my.zip и будет содержать в себе файлы из каталога ToZip.

Создается экземпляр класса FastZip, после чего вызывается один из вариантов метода CreateZip, в данном случае первый параметр – имя бушующего архива, второй – имя директории, которую нужно поместить в архив, далее булево значение рекурсивного поиска файлов, и fileFilter, основанный на регулярном выражении.

С разархивированием все так же просто:

  1. zip.ExtractZip("my.zip""New"null);  


первый параметр – файл архива, второй – каталог, куда будет разархвивирован архив(даже если его не существует, как в данном случае, FatZip создаст его), третий – fileFilter.


Адаптируем под .net compact


Сделаем адаптацию на первом примере. Создадим новый compact проект Device Application, добавим ссылку на SharpZipLib библиотеку (из папки netcf-20) и заменим автоматически сгенерированный код на:

  1. using System;  
  2. using System.Text;  
  3. using System.Windows.Forms;  
  4. using System.IO;  
  5. using ICSharpCode.SharpZipLib.Zip;  
  6.   
  7. namespace CompactZipStreams  
  8. {  
  9.     public partial class Form1 : Form  
  10.     {  
  11.         public Form1()  
  12.         {  
  13.             InitializeComponent();  
  14.         }  
  15.   
  16.         private void Form1_Load(object sender, EventArgs e)  
  17.         {  
  18.             MemoryStream memoryStream = new MemoryStream();  
  19.             FillStream(memoryStream, 100);  
  20.   
  21.             MemoryStream zippedMemoryStream = new MemoryStream();  
  22.             ZipOutputStream zipStream = new ZipOutputStream(zippedMemoryStream);  
  23.             ZipEntry zipEntry = new ZipEntry("myData");  
  24.             zipStream.PutNextEntry(zipEntry);  
  25.             FillStream(zipStream, 100);  
  26.             zipStream.CloseEntry();  
  27.   
  28.             ZipInputStream zipInputStream = new ZipInputStream(zippedMemoryStream);  
  29.             zippedMemoryStream.Position = 0;  
  30.             MemoryStream fromZippedMemoryStream = new MemoryStream();  
  31.             ZipEntry entry = zipInputStream.GetNextEntry();  
  32.             byte[] outputBuffer = new byte[zipInputStream.Length];  
  33.             zipInputStream.Read(outputBuffer, 0, outputBuffer.Length);  
  34.             fromZippedMemoryStream.Write(outputBuffer, 0, outputBuffer.Length);  
  35.   
  36.             MessageBox.Show(string.Format("Изначальная длинна: {0} bytes \n В архиве: {1} bytes \n Разархивированно в: {2} ", memoryStream.Length,zippedMemoryStream.Length,fromZippedMemoryStream.Length));  
  37.   
  38.   
  39.         }  
  40.         private static void FillStream(Stream stream, int times)  
  41.         {  
  42.             string temp = "The quick brown fox jumps over the lazy dog";  
  43.             string data = string.Empty;  
  44.             for (int i = 0; i < times; i++) data += temp;  
  45.             byte[] buffer = System.Text.Encoding.UTF8.GetBytes(data);  
  46.             stream.Write(buffer, 0, buffer.Length);  
  47.         }  
  48.     }  
  49. }  


Как можно заметить: изменилась только ссылка на библиотеку и способ вывода.
Аналогично и со всеми другими примерами, код которых можно найти в исходниках к данной статье.


Заключение

В данной статье была поверхностно рассмотрена работа с классами ZipOutputStream, ZipInputStream, ZipEntry и FastZip из популярной библиотеки SharpZipLib. Для более полного ознакомления используйте официальную документацию, вы сможете увеличить или уменьшить степень сжатия архива, задать пароль, добавить комментарии к архиву, тестировать и изменять существующие архивы. К тому же данная библиотека позволяет работать не только с zip архивами.

Александр Кобелев aka Megano