SharpZlibを使って圧縮/解凍

ファイルの圧縮/解凍をしたいという要求があったので、SharpZlibを使って実験してみました(・∀・)


ZIP圧縮についてはJ#のランタイムにjava.util.zipとかもあるんですが、これは色々と問題もあるらしいということで(´・ω・`)
で、.NETで圧縮/解凍といえばSharpZlibだと思ったので使ってみることにしました。
ちなみに、それ以外の選択肢となるとZlibラッパーを作るとかでしょうか?
とか言っていたら、こんなんのも見つけたり。
http://www.componentace.com/ZLIB.NET


で、特定フォルダ下にあるファイルの圧縮と、圧縮したファイルの解凍ができれば良いということなので、以下のようなサンプルを作ってみました。


まず、特定フォルダ下にあるファイル一覧の取得処理を作っておきます。
再帰的にファイル一覧をArrayListに入れる処理ですね。

private static void ListupFiles(string path, ArrayList list)
{
    string[] subdirs = Directory.GetDirectories( path );
    foreach( string subdir in subdirs) 
    {
        ListupFiles( subdir, list );
    }

    string[] files = Directory.GetFiles( path );
    foreach( string file in files ) 
    {
        list.Add( file );
    }
}

で、取得したファイル一覧のZIPファイルを作成する処理はこんなんで良いようです。

public static void Zip(string zipfile, string baseFolder)
{
    ArrayList list = new ArrayList();

    ListupFiles( baseFolder, list );

    Zip( zipfile, baseFolder, (string[])list.ToArray( typeof(string) ) );
}

public static void Zip(string zipfile, string baseFolder, string[] files)
{
    Crc32 crc = new Crc32();

    string baseFoler = Path.GetFullPath( baseFolder );

    using( FileStream fs = new FileStream( zipfile, FileMode.Create, FileAccess.Write, FileShare.Write ) )
    using( ZipOutputStream zos = new ZipOutputStream( fs ) )
    {
        zos.SetLevel( 6 );

        foreach( string file in files )
        {
            string filename = Path.GetFullPath( file );
            if ( filename.StartsWith( baseFoler ) == true )
            {
                filename = filename.Substring( baseFoler.Length );
            }
            else
            {
                filename = Path.GetFileName( file );
            }

            ZipEntry ze = new ZipEntry( filename );

            using( FileStream fis = new FileStream( file, FileMode.Open, FileAccess.Read, FileShare.Read ) )
            {
                byte[] buffer = new byte[ fis.Length ];
                fis.Read( buffer, 0, buffer.Length );
                fis.Close();

                crc.Reset();
                crc.Update( buffer );

                ze.Crc = crc.Value;
                ze.Size = buffer.Length;
                ze.DateTime = DateTime.Now;

                zos.PutNextEntry( ze );
                zos.Write( buffer, 0, buffer.Length );
            }
        }

        zos.Close();
        fs.Close();
    }
}

ZIPファイル中のファイル名と、物理的なファイル名の使い分けが注意点でしょうか?(・∀・)


で、解凍処理はこんな感じで良いようです。

public static void Unzip(string zipfile, string extractPath)
{
    extractPath = Path.GetFullPath( extractPath );

    using( FileStream fs = new FileStream( zipfile, FileMode.Open, FileAccess.Read, FileShare.Read ) )
    using( ZipInputStream zis = new ZipInputStream( fs ) )
    {
        ZipEntry ze;
        while( ( ze = zis.GetNextEntry() ) != null )
        {
            if ( ze.IsDirectory == true )
            {
                continue;
            }

            string filename = Path.GetFileName( ze.Name );

            string dstPath = extractPath + Path.GetDirectoryName( ze.Name );
            Directory.CreateDirectory( dstPath );

            string dstFile = Path.Combine( dstPath, filename );

            using( FileStream fos = new FileStream( dstFile, FileMode.Create, FileAccess.Write, FileShare.Write ) )
            {
                byte[] buffer = new byte[ 4096 ];
                int length;
                while( ( length = zis.Read( buffer, 0, buffer.Length ) ) > 0 )
                {
                    fos.Write( buffer, 0, length );
                }
                fos.Close();
            }
        }

        zis.Close();
        fs.Close();
    }
}

ファイルの展開先については、指定されたフォルダにZIP中のパスを取得して、Directory.CreateDirectory()してそこに解凍しています。