sticky
Архивирование, разархивирование потоков, файлов и папок с использованием 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 и добавим в код

     

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

     

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

     

    MemoryStream memoryStream = new MemoryStream();
    

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

    FillStream(memoryStream,10);

    Код метода FillStream:

     

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

     

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

     

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

     

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

     

    MemoryStream zippedMemoryStream = new MemoryStream();
    ZipOutputStream zipStream = new ZipOutputStream(zippedMemoryStream);

     

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

     

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

     

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

    FillStream(zipStream,10); 

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

     

    zipStream.CloseEntry();
    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, конструктор которого принимает заархивированный поток.

     

    ZipInputStream zipInputStream = new ZipInputStream(zippedMemoryStream); 

     

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

     

    zippedMemoryStream.Position = 0;

     

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

     

    MemoryStream fromZippedMemoryStream = new MemoryStream();

     

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

     

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

     

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

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

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

     

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

     

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

     

    CreateFilesAndDirectiries();

     

    Код метода:

     

    private static void CreateFilesAndDirectiries()
    {
      List<FileStream> files = new List<FileStream>();
      files.Add(File.Create("file1.txt"));
      files.Add(File.Create("file2.txt"));
      files.ForEach(FillHelloWorldInStream);
      files.ForEach(fStream => fStream.Close());
    }
    private static void FillHelloWorldInStream(FileStream file)
    {
      byte[] buffer = Encoding.UTF8.GetBytes("Hello World");
      file.Write(buffer, 0, buffer.Length);
    }

     

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

     

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

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

     

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

     

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

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

    Код метода:

     

    private static void CreateFileZipEntry(ZipOutputStream zipOutStream, string name, string filePath)
    {
      ZipEntry fileZipEntry = new ZipEntry(name);
      zipOutStream.PutNextEntry(fileZipEntry);
      FileStream fileStram = File.Open(filePath, FileMode.Open);
      byte[] buffer = new byte[fileStram.Length];
      fileStram.Read(buffer, 0, buffer.Length);
      zipOutStream.Write(buffer, 0, buffer.Length);
      zipOutStream.CloseEntry();
    }

     

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

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

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

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

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

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

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

     

    Directory.CreateDirectory("ZipOutPut");

     

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

     

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

     

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

     

    ZipEntry zipEntryFromZippedFile = zipInputStream.GetNextEntry();

     

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

     

    while (zipEntryFromZippedFile != null)
    {
      if (zipEntryFromZippedFile.IsFile)
       {
       FileInfo fInfo = new FileInfo(string.Format("ZipOutPut\\{0}", zipEntryFromZippedFile.Name));
       if (!fInfo.Directory.Exists) fInfo.Directory.Create();          
       FileStream file = fInfo.Create();
       byte[] bufferFromZip = new byte[zipInputStream.Length];
       zipInputStream.Read(bufferFromZip, 0, bufferFromZip.Length);
       file.Write(bufferFromZip, 0, bufferFromZip.Length);
       file.Close();       
       }
      zipEntryFromZippedFile = zipInputStream.GetNextEntry();
    }
    zipInputStream.Close();

     

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

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

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

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

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

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

     

     

    private static void CreateFilesAndDirectiries()
    {
      Directory.CreateDirectory("ToZip");
      List<FileStream> files = new List<FileStream>();
      files.Add(File.Create("ToZip\\file1.txt"));
      files.Add(File.Create("ToZip\\file2.txt"));
      files.ForEach(FillHelloWorldInStream);
      files.ForEach(fStream => fStream.Close());
    }

     

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

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

     

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

     

    Вот и все ;)

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

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

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

     

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

     

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

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

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

     

    using System;
    using System.Text;
    using System.Windows.Forms;
    using System.IO;
    using ICSharpCode.SharpZipLib.Zip;
    
    namespace CompactZipStreams
    {
      public partial class Form1 : Form
      {
        public Form1()
        {
          InitializeComponent();
        }
    
        private void Form1_Load(object sender, EventArgs e)
        {
          MemoryStream memoryStream = new MemoryStream();
          FillStream(memoryStream, 100);
    
          MemoryStream zippedMemoryStream = new MemoryStream();
          ZipOutputStream zipStream = new ZipOutputStream(zippedMemoryStream);
          ZipEntry zipEntry = new ZipEntry("myData");
          zipStream.PutNextEntry(zipEntry);
          FillStream(zipStream, 100);
          zipStream.CloseEntry();
    
          ZipInputStream zipInputStream = new ZipInputStream(zippedMemoryStream);
          zippedMemoryStream.Position = 0;
          MemoryStream fromZippedMemoryStream = new MemoryStream();
          ZipEntry entry = zipInputStream.GetNextEntry();
          byte[] outputBuffer = new byte[zipInputStream.Length];
          zipInputStream.Read(outputBuffer, 0, outputBuffer.Length);
          fromZippedMemoryStream.Write(outputBuffer, 0, outputBuffer.Length);
    
          MessageBox.Show(string.Format("Изначальная длинна: {0} bytes \n В архиве: {1} bytes \n Разархивированно в: {2} ", 
    
    memoryStream.Length,zippedMemoryStream.Length,fromZippedMemoryStream.Length));
    
    
        }
        private static void FillStream(Stream stream, int times)
        {
          string temp = "The quick brown fox jumps over the lazy dog";
          string data = string.Empty;
          for (int i = 0; i < times; i++) data += temp;
          byte[] buffer = System.Text.Encoding.UTF8.GetBytes(data);
          stream.Write(buffer, 0, buffer.Length);
        }
      }
    }

     

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

    Заключение

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

     

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

    • Перемещено I.VorontsovModerator 2 октября 2010 г. 5:59 (От:Форум по .NET Framework)
    • Изменено Megano 8 февраля 2011 г. 12:32 Подправил линк на исходники

Все ответы