zipファイルのプレビュー


zip圧縮を使って、ロード処理を減らしたかったので、zipファイルの展開を試してみた。

「Load server file」をクリックすると、サーバー上のzipファイルを読んでプレビューする。
「Load Local file」をクリックすると、ローカルPC上のzipファイルを読んでプレビューする(ファイルをサーバー側に送るわけではない)。

動作確認用zipファイル
http://www.mztm.jp/wp/wp-content/uploads/2010/10/testfile.zip

今、たくさんの画像ファイルを読み込むコンテンツを作っているんだけど、本番はともかく、試作版も全部読み込み処理を走らせるのもなんかかったるいような気がしたし、関係者への配布やプロジェクトサイトの管理上もなるたけファイル数を減らしたい。そんなわけで、zipファイルを使うことにした。

最初にFZipを試した。
FZip - côdeazur brasil lab
しかし、どうも手元の環境(WindowsVistaの標準のzip)で圧縮したファイルがうまく読み込めない。自分以外の人と手軽に使うことも念頭においているので、めんどくさいことにはしたくないので、あきらめた。

で、本命、nochump.comを試した。
AS3 Zip Library Release « nochump.com
んだけど、噂通り展開にとても時間がかかる。

nochumpさんのzipライブラリ - 赤の女王の香箱
の対処法を試したら、確かに早くなったので、採用。

ZipFile.asの105行目あたりのコメントアウトを外す。でも、「Security.sandboxType == Security.APPLICATION」はAIR(旧称apollo)でしか動かないので、swfだとダメ。
[sourcecode language="as3" firstline="102"]
case ZipConstants.DEFLATED:
/*
if(Security.sandboxType == Security.APPLICATION) {
// apollo environment
b1.inflate();
return b1;
}
/**/
var b2:ByteArray = new ByteArray();
var inflater:Inflater = new Inflater();
inflater.setInput(b1);
inflater.inflate(b2);
return b2;
[/sourcecode]
なので、この判定自体にかかわらず、ByteArrayのinflate()が動くようにしたら、あっさり高速になった。
[sourcecode language="as3" firstline="102"]
case ZipConstants.DEFLATED:
/*
if(Security.sandboxType == Security.APPLICATION) {
// apollo environment
b1.inflate();
return b1;
}
/**/
b1.inflate();
return b1;

var b2:ByteArray = new ByteArray();
var inflater:Inflater = new Inflater();
inflater.setInput(b1);
inflater.inflate(b2);
return b2;
[/sourcecode]

とりあえず、結果オーライでこのままいくことにした。
ただ、nochumpの件のページ(http://nochump.com/blog/archives/15)には、わざわざ遅くなる処理を入れている理由らしきことが書いてあるようなのだが、よくわかんないので、スルー。まあ、Adobe純正の展開(ByteArray.inflate())を使うようにしているんだから、あんまりひどいことにはならないんじゃないかな、だといいな。

▼参考
GRAM | グラム » ZipされたJPEGを扱う(複数)

nochumpさんのzipライブラリ - 赤の女王の香箱

ActionScript 3.0 でZIPの圧縮と解凍 (Unknown Quality)

▼wonderfl
よく考えたら、wonderflでもほぼそのままで動くじゃん、ということで作ってみた。
ただし、上記のコメントアウトの高速化、とかは対応してない。当たり前だけど。
Zipファイルのプレビュー(wonderfl版)

▼ActionScript AS3(FP10)
[sourcecode language="as3"]
package {
import com.bit101.components.*;
import flash.display.Sprite;
import flash.display.Loader;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.net.FileReference;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.utils.IDataInput;
import nochump.util.zip.*;

public class Main extends Sprite {
private var _loadedData:IDataInput;
private var _fileReference:FileReference;
private var _inputText:InputText;
private var _previewTF:TextArea;
private var _previewSprite:Sprite;
private var _list:List;
private var _listItems:Array;

public function Main() {
initServer();
initLocal();
initPreview();
}
private function initLocal():void {
new PushButton(this, 2, 32, "Load Local file", atLoadLocalFile);
}
private function atLoadLocalFile(event:Event):void {
_fileReference = new FileReference();
_fileReference.addEventListener(Event.SELECT, atSelected);
_fileReference.browse();
}
private function atSelected(event:Event):void {
if (fileTypeCheck(_fileReference.type)) {
return;
}
_fileReference.addEventListener(Event.COMPLETE, loadComp);
_fileReference.load();
}

private function initServer():void {
var urlStr:String = (this.loaderInfo.url);
//wordpressだとフルパスじゃないとダメみたいなので
urlStr = urlStr.substr(0, urlStr.lastIndexOf("/")) + "/";
_inputText = new InputText(this, 104, 4, urlStr + "testfile.zip");
_inputText.width = 488;
new PushButton(this, 2, 2, "Load server file", atLoadServerFile);
}
private function atLoadServerFile(event:Event):void {
var urlStr:String = _inputText.text;
urlStr = urlStr.substr(urlStr.lastIndexOf("."));
if (fileTypeCheck(urlStr)) {
return;
}

var loader_obj:URLLoader = new URLLoader();
loader_obj.addEventListener(Event.COMPLETE, loadComp);
loader_obj.dataFormat = "binary";
loader_obj.load(new URLRequest(_inputText.text));
}

private function fileTypeCheck(urlStr:String):Boolean {
var result:Boolean;
urlStr = urlStr.toLowerCase();
if (urlStr != ".zip" && urlStr != ".swc") {
_previewSprite.visible = false;
_previewTF.visible = true;
_previewTF.text = "zip または swcファイルの展開に対応しています。";
result = true;
}
return result;
}

private function loadComp(event:Event):void {
_list.items = [];
_listItems = [];
var zipFile:ZipFile = new ZipFile(event.target.data);
var n:int = zipFile.entries.length;
for(var i:int = 0; i < n; i++) {
var entry:ZipEntry = zipFile.entries[i];
var date:Date = new Date();
date.setTime(entry.time);
_list.addItem(dateFormat(date) + " : " + sizeFormat(entry.size) + " : " + entry.name + "\n");

if (entry.isDirectory()) {
_listItems[i] = "Directory";
}else {
var loader:Loader;
var type:String = typeFormName(entry.name);
switch (type) {
case "image":
loader = new Loader();
loader.loadBytes(zipFile.getInput(entry));
//loader.contentLoaderInfo.addEventListener(Event.COMPLETE, function():void { trace("comp") } );
_listItems[i] = loader;
break;
case "text":
_listItems[i] = zipFile.getInput(entry).toString();
break;
default:
_listItems[i] = type;
}
}
}
if (n > 0) {
_list.selectedIndex = 0;
atClickList(null);
}
}
private var EXTENSIONS:Object = {
//"swf":["swf"], "movie":["mp4", "flv"],未対応
"image":["jpg", "jpeg", "gif", "png"],
"text":["txt", "js", "xml", "php", "asp", "as", "html", "htm", "php", "py", "mxml"]
};
private function typeFormName(name:String):String {
var str:String = name.substr(name.lastIndexOf(".") + 1).toLowerCase();
var extension:String;
for (var p:String in EXTENSIONS) {
var n:int = EXTENSIONS[p].length;
for (var i:int = 0; i < n; i++) {
extension = EXTENSIONS[p][i];
if(extension == str){
str = p;
break;
}
}
}
return str;
}
private function dateFormat(date:Date):String {
var result:String = "";
result += String(date.getFullYear());
result += "/" + String(date.getMonth() + 1);
result += "/" + String(date.getDate());
result += " " + String(date.getHours());
result += ":" + String(date.getMinutes());
result += "." + String(date.getSeconds());
return result;
}

private function sizeFormat(byte:int):String {
var length:int = byte.toString().length;
var result:String;
if (length < 4) {
result = String(byte) + "Byte";
}else if(length < 7){
result = String(Math.round(byte / 1024)) + "KB";
}else{
result = String(Math.round(byte / (1024 * 1024) * 100) / 100) + "MB";
}
return result;
}

private function initPreview():void {
//bit101の日本語対応
Style.embedFonts = false;
Style.fontName = "_typewriter";
Style.fontSize = 12;

_previewTF = new TextArea(this, 0, 165);
_previewTF.width = 594;
_previewTF.height = 295;
_list = new List(this, 0, 60);
_list.width = 594;
_list.addEventListener(MouseEvent.CLICK , atClickList);
_previewSprite = new Sprite();
_previewSprite.y = 165;
this.addChild(_previewSprite);
}
private function atClickList(event:Event):void {
while (_previewSprite.numChildren > 0) {
_previewSprite.removeChildAt(0);
}

if (typeof _listItems[_list.selectedIndex] == "string") {
_previewTF.visible = true;
_previewTF.text = _listItems[_list.selectedIndex];
_previewSprite.visible = false;
}else if (typeof _listItems[_list.selectedIndex] == "object"){
_previewTF.visible = false;
_previewSprite.visible = true;
_previewSprite.addChild(_listItems[_list.selectedIndex]);
}
}
}
}

[/sourcecode]