Зачем пишем
- потому что удобно иметь свой настраиваемый инструмент, в котором можно вмешаться в архивацию на любом этапе
- потому что это интересно
- потому что многие архиваторы имеющие api, платные, а насчет других см. первый аргумент.
Технологии и библиотеки
Понадобится библиотека zlib.net.dll (официальный сайт).
Среда разработки Visual Studio 2010
Язык C#
Framework 3.5
Техническое задание
Архиватор должен уметь:
- сжимать файлы и каталоги
- собирать архив без сжатия
- шифровать данные (со сжатием и без)
- исключать указанные пути
- удалять файлы после их сжатия
- распаковывать сжатый архив
Проектирование
Формат архива
Путем оптимизации пришел к следующему варианту:
Назначение |
Размер |
Тип архива | 1 байт |
Длина заголовка(после сжатия и шифрования) | 4 байта |
Заголовок(подробнее рассмотрим ниже) | N байт |
Блок содержимого первого файла | N байт |
Блок содержимого второго файла | N байт |
...... | ...... |
Блок содержимого K-того файла | N байт |
Формат заголовка архива
Назначение |
Размер |
Размер необработанного заголовка | 4 байта |
Блок 1 | N байт |
Блок 2 | N байт |
...... | ...... |
Блок K | N байт |
Формат блока заголовка архива
Назначение |
Размер |
Размер блока | 4 байта |
Длина абсолютного пути | 4 байта |
Абсолютный путь | N байт |
Длина относительного пути | 4 байта |
Относительный путь | N байт |
Размер объекта после обработок | 8 байт |
Немного пояснений. В начале файла архива хранится заголовок, в котором собраны все метаданные по объектам архива. Сам заголовок проходит те же стадии сжатия и шифрования что и файлы архива. После заголовка идут блоки хранящие содержимое файлов после обработки, блоки идут вплотную. Определение границ блока следует из заголовка, в котором хранятся размеры блоков.
Общие принципы работы
Пользователь задает опции сжатия, на основе который подключаются необходимые обработчики файлов (архиватор, шифратор), каждый такой обработчик содержит два метода, Execute и BackExecute. При архивации вызываем метод Execute, при разархивации метод BackExecute, причем при разархивации используем обработчики в обратном порядке. Такая структура позволяет крайне просто дополнить программу любым количеством новых обработчиков (например реализующих другие методы шифрования или сжатия).
Алгоритм работы
- Определение типа архива(сжатый, зашифрованный)
- Чтение списка объектов архивации
- Формирование на основе прочитанного списка и списка исключений полного списка архивируемых объектов
- Создание заголовка архива (в объектном виде)
- Перебор полного списка объектов лежащих в заголовке
- Обработка объекта, обновление данных о его размере после обработки в заголовке, запись во временный файл обработанного содержимого.
- Сохранение заголовка в файл
- Обработка заголовка(сжатие, шифрование)
- Сборка конечного файла архива
Реализация
ZLib умеет сжимать/распаковывать переданные ему данные в виде массива байт. Собственно это все что нам нужно и все что будет использовать. Шифровать данные он не умеет, для этого воспользуемся стандартной библиотекой .NET Framework — System.Security.Cryptography.
В процессе архивации/разархивации можно получать данные по текущему обрабатываемому объекту, а также возникшие ошибки.
В случае получения ошибки при обработке файла, пользователю предлагается на выбор 4 действия:
- прервать выполнение
- игнорировать ошибку
- игнорировать все ошибки
- повторить
Запрос действия можно отменить, просто закомментировав событие ErrorProcessing, в этом случае выполнение программы прервется.
Код программы приводить не буду, даю ссылку на исходники.
Напрямую:
Проект
В виде dll'ки
SVN:
svn://svn.code.sf.net/p/yark/code-0/trunk
Проект:
sourceforge.net/projects/yark
И пример использования:
Сжатие
ArchiveProvider compressor = new ArchiveProvider();
using (SaveFileDialog sfd = new SaveFileDialog())
{
if (sfd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
CompressorOption option = new CompressorOption()
{
Password = пароль_если_зашифровать,
WithoutCompress = true_если_без_сжатия,
RemoveSource = true_если_удалять_исходные_файлы,
Output = sfd.FileName
};
//Списки файлов и каталогов для сжатия
foreach (string line in lbIncludes.Items)
option.IncludePath.Add(line);
//Списки файлов и каталогов для исключения
foreach (string line in lbExclude.Items)
option.ExcludePath.Add(line);
compressor.Compress(option);
}
}
Разархивация
ArchiveProvider decompressor = new ArchiveProvider();
using (FolderBrowserDialog fbd = new FolderBrowserDialog())
{
if (fbd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
decompressor.Decompress(путь_к_архиву, fbd.SelectedPath, пароль_если_зашифрован);
}
}
Сравнение результата работы
По времени результат засекать не стал, примерно одинаково.
Исходные данные:
- каталог с текстовыми файлами (1 430 Кб)
- каталог со смешанными данными (18 893 Кб)
Текст |
Смешанные данные |
|
WinRar |
613 | 8 045 |
Zip |
638 | 8 709 |
Этот |
588 | 8 655 |
Для rar и zip формата был выставлен параметр обычного сжатия, который используется и в программе.
В текущем формате архива хранятся абсолютные пути файлов и каталогов, можно исключить их и немного улучшить сжатие.
Возможные улучшение
- сохранение информации о файле (дата создания/изменения, права доступа)
- добавить многопоточность (достаточно просто распараллелить создание временных файлов)
- добавлять комментарии к архиву
- ассоциировать файлы с программой