ZIP形式でファイルアーカイブする (SharpZipLib 利用)

今回やりたかったことは、ASP.NET でのダウンロードするために1ファイルを無理矢理ZIP形式にしたい、という要望だったので最終的にかなりサボっているところがある。

ZIP書庫の作成(圧縮)、展開(解凍)、閲覧を行う (dobon.net) を参考に始めたのだが、CompressedSize に適切な値を入れていないことによって正しいZIPファイルが作成できなかった。このサイトのコメントでも CompressedSize に設定していないことによりエラーが発生していることが報告されている。

Deflate などで圧縮したい場合には、一度ダミーStreamに向かって圧縮を実行し、圧縮後のサイズを求めてから改めて書き込むという手順になる。とはいえASP.NETでZIPにしたいという場面においては、中に入れるファイルは jpg,png,pdf などすでに圧縮済みのファイルフォーマットであることが多く、圧縮せずにただのアーカイブ方式としてZIPを使うという状況が今後もほとんどなのではないかと思われる。

そういう意味ではCompressionMethod = CompressionMethod.Stored かつ CompressedSize = Size という使い方で困ることはあまりなさそうだ。

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

namespace SampleLib
{
    public class Zip作成
    {
        private const int バッファサイズ = 4096;
        private readonly Crc32 crc = new Crc32();
        public Stream 書き込み先 { get; set; }

        /// <summary>
        /// inputStream 1ファイル分 ZIPファイル相当のバイナリを書き込む
        /// </summary>
        /// <param name="ファイル名"></param>
        /// <param name="inputStream"></param>
        public void 実行(string ファイル名, Stream inputStream)
        {
            Stream stream = 書き込み先;
            long crcValue;
            long filelen;
            Crc取得(inputStream, out crcValue, out filelen);

            ZIP実行Sub(stream, ファイル名, inputStream, crcValue, filelen, filelen);
            // deflate などで圧縮したい場合は、一度圧縮後のサイズを求めてから実行する。おおよそ次のような感じ
            // long 圧縮後サイズ = ZIP実行Sub(dummystream, ファイル名, inputStream, crcValue, filelen, -1);
            // if (圧縮後サイズ > 0) ZIP実行Sub(stream, ファイル名, inputStream, crcValue, filelen, 圧縮後サイズ);
        }

        /// <summary>
        /// ZIPの格納にあたっては、一度圧縮後のサイズを求めてから、それを CompressedSize に入れるべきのようだ
        ///  ここでは CompressionMethod.Stored すなわち 圧縮なし で格納するので元のファイルサイズと同じサイズとなる
        /// </summary>
        private long ZIP実行Sub(Stream stream, string ファイル名, Stream inputStream, long crcValue, long ファイルサイズ, long 圧縮後サイズ)
        {
            using (var zos = new ZipOutputStream(stream))
            {
                // zos.SetLevel(0); // 圧縮しないので、圧縮レベルを指定しない
                var ze = new ZipEntry(ファイル名)
                             {
                                 Crc = crcValue,
                                 Size = ファイルサイズ,
                                 DateTime = DateTime.Now,
                                 CompressionMethod = CompressionMethod.Stored
                             };
                if (圧縮後サイズ > 0) ze.CompressedSize = 圧縮後サイズ;
                zos.PutNextEntry(ze);

                // 実際の中身を追加する
                {
                    int len;
                    var buffer = new byte[バッファサイズ];
                    while ((len = inputStream.Read(buffer, 0, バッファサイズ)) > 0)
                    {
                        zos.Write(buffer, 0, len);
                    }
                }
                zos.CloseEntry(); // エントリを閉じる
                // エントリを閉じると、圧縮後のサイズが判明する
                return ze.CompressedSize;
            }
        }

        private void Crc取得(Stream inputStream, out long crcValue, out long filelen)
        {
            int len;
            var buffer = new byte[バッファサイズ];
            filelen = 0;
            crc.Reset();
            while ((len = inputStream.Read(buffer, 0, バッファサイズ)) > 0)
            {
                crc.Update(buffer, 0, len);
                filelen += len; // ファイル長を追加
            }
            inputStream.Seek(0, SeekOrigin.Begin); // ストリームを先頭にまき戻しておく
            crcValue = crc.Value;
            return;
        }
    }
}

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中

%d人のブロガーが「いいね」をつけました。