Инструментарий: 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;
using ICSharpCode.SharpZipLib.Zip;
using System.IO;
Теперь создадим эталонный поток, с которым в последствии сверим сжатый поток.
- MemoryStream memoryStream = new MemoryStream();
MemoryStream memoryStream = new MemoryStream();
Заполним его данными с помощью метода FillStream
- FillStream(memoryStream,10);
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);
- }
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));
Console.WriteLine(string.Format("Изначальная длинна потока: {0} bytes", memoryStream.Length));
Создадим экземпляр класса ZipOutputStream, в своем конструкторе он принимает единственный аргумент – поток, в который он будет сжимать данные.
- MemoryStream zippedMemoryStream = new MemoryStream();
- ZipOutputStream zipStream = new ZipOutputStream(zippedMemoryStream);
MemoryStream zippedMemoryStream = new MemoryStream();
ZipOutputStream zipStream = new ZipOutputStream(zippedMemoryStream);
Теперь нам необходимо ( в противном случае мы получим InvalidOperationException:No Open Entry, при попытке записи данных в поток) добавить запись в оглавление архива, представленное классом ZipEntry, один из конструкторов которого принимает строку, как имя.
- ZipEntry zipEntry = new ZipEntry("myData");
- zipStream.PutNextEntry(zipEntry);
ZipEntry zipEntry = new ZipEntry("myData");
zipStream.PutNextEntry(zipEntry);
Поток готов к заполнению данными, заполним его аналогично memoryStream.
- FillStream(zipStream,10);
FillStream(zipStream,10);
После чего закроем ZipEntry
- zipStream.CloseEntry();
- Console.WriteLine(string.Format("Длинна потока в архиве: {0} bytes", zippedMemoryStream.Length));
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);
ZipInputStream zipInputStream = new ZipInputStream(zippedMemoryStream);
Так как мы уже работали с zippedMemoryStream, переставим курсор в начало.
- zippedMemoryStream.Position = 0;
zippedMemoryStream.Position = 0;
После этого создадим в памяти новый поток для извлечения результатов.
- MemoryStream fromZippedMemoryStream = new MemoryStream();
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));
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;
using ICSharpCode.SharpZipLib.Zip;
using System.IO;
Далее вызовем метод
- CreateFilesAndDirectiries();
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);
- }
- </filestream></filestream>
private static void CreateFilesAndDirectiries()
{
List files = 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"));
ZipOutputStream zipOutStream = new ZipOutputStream(File.Create("my.zip"));
После чего вызовем метод CreateFileZipEntry.
- CreateFileZipEntry(zipOutStream, "file1.txt", "file1.txt");
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();
- }
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");
CreateFileZipEntry(zipOutStream, @"folder1\folder2\folder3\file2.txt", "file2.txt");
В результате этого вызова в архиве будет создана иерархия каталогов, в последнем из которых будет находиться file2.txt.
Все необходимые данные переданы в архив, после чего вызываем
zipOutStream.Close();
Теперь разархивируем только что созданный нами архив.Создадим директорию в папке приложения, куда разархивируем данные.
- Directory.CreateDirectory("ZipOutPut");
Directory.CreateDirectory("ZipOutPut");
Создадим экземпляр класса ZipInputStream и передадим в его конструктор экземпляр FileStream, нашего архива.
- ZipInputStream zipInputStream = new ZipInputStream(File.Open("my.zip", FileMode.Open));
ZipInputStream zipInputStream = new ZipInputStream(File.Open("my.zip", FileMode.Open));
Получим первую запись из заголовка архива.
- ZipEntry zipEntryFromZippedFile = zipInputStream.GetNextEntry();
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();
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;
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());
- }
- </filestream></filestream>
private static void CreateFilesAndDirectiries()
{
Directory.CreateDirectory("ToZip");
List files = 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);
FastZip zip = new FastZip();
zip.CreateZip("my.zip","ToZip", true,null);
Вот и все ;)
В папке приложения будет создан архив с именем my.zip и будет содержать в себе файлы из каталога ToZip.
Создается экземпляр класса FastZip, после чего вызывается один из вариантов метода CreateZip, в данном случае первый параметр – имя бушующего архива, второй – имя директории, которую нужно поместить в архив, далее булево значение рекурсивного поиска файлов, и fileFilter, основанный на регулярном выражении.
С разархивированием все так же просто:
- zip.ExtractZip("my.zip", "New", null);
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);
- }
- }
- }
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