Illustratorにzipファイルを展開させてみる

まあ、別にIllustratorである必要はまったくありません。それにAIRのお手本とそう大差ありません。Indesign等の他のCSファミリーからでもマニフェストとか記述すればそのまま動きますから、こういったものは。
もうひとつ言ってしまうと、実際に運用を目指すのなら各種AIRやFlex向けにリリースされたライブラリ群を利用するのが手っ取り早い方法です。実際にググってみると簡単に幾つか見つかる訳ですから。
しかし、一通りやってみない事には気が済みませんのでファイル構造等を解説しつつ作ってみたいと思います。

まずは簡単にファイル構造解説しておきましょう。以下、pkwareからの引用です

ZIP format
Byte order: Little-endian

バイトオーダーはリトルエンディアンです。要するに複数バイトのストリームは下位から書いていくって事ですね。
ASのbyteArreyにおいてもエンディアンを指定可能になっています。

Overall zipfile format:
[Local file header + Compressed data [+ Extended local header]?]*
[Central directory]*
[End of central directory record]

Local file header:
Offset   Length   Contents
  0      4 bytes  Local file header signature (0x04034b50)
  4      2 bytes  Version needed to extract
  6      2 bytes  General purpose bit flag
  8      2 bytes  Compression method
 10      2 bytes  Last mod file time
 12      2 bytes  Last mod file date
 14      4 bytes  CRC-32
 18      4 bytes  Compressed size (n)
 22      4 bytes  Uncompressed size
 26      2 bytes  Filename length (f)
 28      2 bytes  Extra field length (e)
        (f)bytes  Filename
        (e)bytes  Extra field
        (n)bytes  Compressed data

以下に示すのはサンプルファイルのヘッダです。赤くハイライトされているのが30バイトのヘッダ部分です。

zipHeader.png

offset 0のシグネチャ部分を見てみると良くわかるのですがバイトオーダが逆に見えますね。リトルエンディアンというのはこういう事です。複数バイトの情報は下位バイトから収容されます。
offset 4は解凍に必要なバージョン情報。続く2バイトが汎用目的のビットフラグ。
offset 8が圧縮メソッド0が非圧縮8が圧縮となります。これは圧縮辞書等の絡みで小容量のファイルは圧縮処理すると逆に容量が増える事があるためです。もちろん、容量が増加する場合は圧縮メソッドを0として圧縮処理は行われません。
offset 10及び12は更新日時が各2バイトで、14はCRC-32のハッシュが4バイトで並びます。
offset 18は4バイトの圧縮サイズ。0ffset 22は非圧縮サイズ4バイトとなります。
offset 26はローカルヘッダ以降に並ぶファイル名の長さを2バイトで、offset 28はファイル名以降に続くエクストラフィールドの長さを2バイトで記述します。
さて、肝心のファイル構造ですが、ファイルごとにローカルファイルヘッダとファイルエントリ(圧縮データ)が一組になっており、これを連続で並べてあります。更にファイルのお尻にはファイルヘッダがファイルの数ぶんだけ続いた後セントラルディレクトリっていうのが続きます。こういった構造がZipファイルの付け足し等のフレキシブルな操作を可能とするわけですが、今回の例では全く関わりのない部分です。

とりあえずmxmlをご覧いただきましょう。詳細はコメント入れてあります。

<?xml version=”1.0″ encoding=”utf-8″?>
<mx:WindowedApplication xmlns:mx=”http://www.adobe.com/2006/mxml&#8221; layout=”vertical”>
    <mx:Script>
        <![CDATA[
            import flash.filesystem.*;
            import flash.utils.ByteArray;
            import flash.events.Event;

            private var bytes:ByteArray = new ByteArray();
            private var fileName:String = new String();
            private var fNmLen:uint;
            private var xtrFldLen:uint;
            private var offset:uint;
            private var cmpSize:uint;
            private var uncompSize:uint;
            private var compMethod:int;
            private var signature:int;
            private var dskTpfile:File = File.desktopDirectory;
            private var zStream:FileStream = new FileStream();

            private function doExt():void
            {
                //get headder information
                var zfile:File = dskTpfile.resolvePath(fileNm.text);
                zStream.open(zfile, FileMode.READ);
                bytes.endian = Endian.LITTLE_ENDIAN;
                while (zStream.position<zfile.size) //loop include files length
                {
                    zStream.readBytes(bytes, 0, 30); //first 30 bytes read (header info)
                    bytes.position = 0;
                    signature = bytes.readInt();
                    if (signature != 0x04034b50) //check fileheadder signature (PK34)
                    {
                        break;
                    }
                    bytes.position = 8;
                    cmpMethod = bytes.readByte();
                    offset = 0;
                    bytes.position = 26;
                    fNmLen = bytes.readShort();//get file name
                    offset += fNmLen; //add length of file name
                    bytes.position = 28;
                    xtrFldLen = bytes.readShort();
                    offset += xtrFldLen; // add length of extra field
                    bytes.position = 18;
                    compSize = bytes.readUnsignedInt(); //get size of compressed portion
                    bytes.position = 22; // offset to uncompressed size
                    uncompSize = bytes.readUnsignedInt(); //get uncompressed size
                    zStream.readBytes(bytes, 30, offset);
                    bytes.position = 30;
                    fileName = bytes.readUTFBytes(flNameLength);
                    messages.text += fileName + “\n”;
                    messages.text += “Compressed size = ” + compSize + ‘\n’;
                    messages.text += “Uncompressed size = ” + uncompSize + ‘\n’;

                    //load data body
                    zStream.readBytes(bytes, 0, compSize);
                    if (cmpMethod == 8) //decompress if file compressed
                    {
                        bytes.uncompress(CompressionAlgorithm.DEFLATE);
                    }
                    saveFile(fileName, bytes);
                }
            }

            private function saveFile(fileName:String, data:ByteArray):void
            {
                var dstFile:File = File.desktopDirectory;   
                var svFile:File = dstFile.resolvePath(fileName);
                var svStream:FileStream = new FileStream();
                svStream.open(svFile, FileMode.WRITE);
                svStream.writeBytes(data, 0, data.length);
                svStream.close();
            }
        ]]>
    </mx:Script>
    &l
t;mx:Form>
        <mx:FormItem label=”decompress”>
            <mx:Button id=”ext” label=”apply” click=”doExt();” />
            <mx:TextInput id=”fileNm” width=”200″ text=”test.zip” />
            <mx:TextArea id=”messages” width=”320″ height=”150″/>
        </mx:FormItem>
    </mx:Form>
</mx:WindowedApplication>

<!–[CDATA[
import flash.filesystem.*;
import flash.utils.ByteArray;
import flash.events.Event;
private var bytes:ByteArray = new ByteArray();
private var fileName:String = new String();
private var fNmLen:uint;
private var xtrFldLen:uint;
private var offset:uint;
private var cmpSize:uint;
private var uncompSize:uint;
private var compMethod:int;
private var signature:int;
private var dskTpfile:File = File.desktopDirectory;
private var zStream:FileStream = new FileStream();
private function doExt():void
{
//get headder information
var zfile:File = dskTpfile.resolvePath(fileNm.text);
zStream.open(zfile, FileMode.READ);
bytes.endian = Endian.LITTLE_ENDIAN;
while (zStream.position

動作的にはローカルファイルヘッダのチェック後ファイルエントリを読み込んで処理、その後次のローカルファイルヘッダを読み込んでの繰り返しとなっています。恐ろしい事にセントラルディレクトリやファイルヘッダの存在は全く無視と言う処理です。それに参照するヘッダ情報は圧縮メソッド、圧縮容量、展開容量、ファイル名、エクストラフィールドの長さだけです。ファイル名とファイルエントリの位置情報さえ取得出来たらあとはファイルエントリを抜き出して展開→ファイル保存という流れをローカルファイルヘッダのシグネチャが読み出せなくなるまで繰り返します。
問題はCRCとかチェックしてませんから書庫が破壊されていた場合トラブります。また、OSXではローカルファイルヘッダ情報の実装が少々異なります。だからOSXが圧縮したものは解凍出来ません。なんだかなぁ〜まあ、Appleらしいです。

広告

コメントを残す

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

WordPress.com ロゴ

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

Google フォト

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

Twitter 画像

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

Facebook の写真

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

%s と連携中