Содержание статьи:
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()
{
Listfiles = new List ();
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");
Listfiles = new List ();
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