Hatena::ブログ(Diary)

After Effects&Flash+αテクニック集 このページをアンテナに追加 RSSフィード

2009-11-22

[][]そうめんを利用して画像を保存する

おはようございます。

金曜の会社の飲み会で、のどをやられたようです。

それにしても寒いですね。先日石油ヒーターを出しました。

さて、今日は前回投稿の画像保存を「そうめん」を利用してやってみたいと思います。


■そうめんとは

BeInteractive!のyossyさんが作ったActionScript Thread Library 1.0(そうめん) です。

使ってみた感想は・・・とても便利です。

いか、良いと思ったことです。

  • addEventListenerとさよならできます。
  • 次に行う処理がはっきりし、可読性が上がります。
  • 簡単に待ち(sleep)処理ができます。
  • 割り込み処理ができる

では、前回投稿の画像保存処理がそうめんを利用するとどうなるか見ていきましょう。


■画像をダウンロードするURLLoaderThread

URLLoaderにThreadの機構を追加した拡張クラスです。

var urlRequest:URLRequest = new URLRequest(url);
var loader:URLLoader = new URLLoader();
loader.dataFormat = URLLoaderDataFormat.BINARY;
loader.addEventListener(Event.COMPLETE, function(e:Event):void{ onLoadComplete(e, savePath); });
loader.load(urlRequest);

これが、下のように変わります。

URLLoaderThreadは、プライベート変数として宣言しています。

private var _imageLoader:URLLoaderThread;
override protected function run():void
{
  _imageLoader = new URLLoaderThread(new URLRequest(_imageUrl));
  _imageLoader.loader.dataFormat = URLLoaderDataFormat.BINARY;
  //URLのコンテンツダウンロードを開始する
  _imageLoader.start();
  //ダウンロード完了まで待機
  _imageLoader.join();
  //ダウンロード完了後にonLoadCompleteメソッドを呼び出す(保存先はコンストラクタでプライベート変数に格納)
  next(onLoadComplete);
}

どうでしょうか?

処理の上から順に読めるので分かりやすく、事前にイベントリスナを登録しておく必要もありません。


■画像の保存処理

保存処理は、基本的にほとんど変わりません。

相違点は、

  • イベントに関連したクラスではないので、メソッドの引数にEvent変数を持ちません
  • ダウンロードコンテンツへのアクセスもURLLoaderにキャストする必要がありません

private function onLoadComplete(e:Event, savePath:String):void
{
  var file:File = new File(savePath);
  var stream:FileStream = new FileStream();
  var byteArr:ByteArray = new ByteArray();
  stream.open(file, FileMode.WRITE);
  stream.writeBytes(URLLoader(e.target).data);
}

これが、下のように変わります。

private function onLoadComplete():void
{
  var file:File = new File(_savePath);
  var stream:FileStream = new FileStream();
  stream.open(file, FileMode.WRITE);
  stream.writeBytes(_imageLoader.loader.data);
  stream.close();
}


■SerialExecutorを使った拡張

画像のダウンロードを連続して行いたい場合はどうすれば良いでしょうか?

その場合、SerialExecutor(ParallelExecutor)を利用します。

SerialExecutorは、登録したThreadを順番に実行してくれます。並列処理を行いたい場合は、ParallelExecutorを利用します。

以下は、SerialExecutorを利用した順次ダウンロードのサンプルです。

var downloaders:SerialExecutor;
downloaders = new SerialExecutor();
//3つの画像ダウンロードスレッドを登録
downloaders.addThread(new DownloadImageThread(url1, savePath1));
downloaders.addThread(new DownloadImageThread(url2, savePath2));
downloaders.addThread(new DownloadImageThread(url3, savePath3));
//ダウンロード開始
downloaders.start();
downloaders.join();
next(completeDownload);


全ソースと使い方は続きのページに載せます。



ソース

package
{
  import flash.events.Event;
  import flash.filesystem.File;
  import flash.filesystem.FileMode;
  import flash.filesystem.FileStream;
  import flash.net.URLLoaderDataFormat;
  import flash.net.URLRequest;
  import flash.utils.ByteArray;

  import org.libspark.thread.Thread;
  import org.libspark.thread.threads.net.URLLoaderThread;

  public class DownloadImageThread extends Thread
  {
    private var _imageUrl:String;
    private var _savePath:String;
    private var _imageLoader:URLLoaderThread;

    /**
     * コンストラクタ
     */
    public function DownloadImageThread(imageUrl:String, savaPath:String):void
    {
      _imageUrl = imageUrl;
      _savePath = savaPath;
    }

    /**
     * スレッド開始
     */
    override protected function run():void
    {
      _imageLoader = new URLLoaderThread(new URLRequest(_imageUrl));
      _imageLoader.loader.dataFormat = URLLoaderDataFormat.BINARY;
      _imageLoader.start();
      _imageLoader.join();
      next(onLoadComplete);
    }

    /**
     * URLのコンテンツをコンテンツを保存する。
     */
    private function onLoadComplete():void
    {
      var file:File = new File(_savePath);
      var stream:FileStream = new FileStream();
      try {
        stream.open(file, FileMode.WRITE);
        stream.writeBytes(_imageLoader.loader.data);
      } catch (e:Error) {
        trace(e.getStackTrace());
      } finally {
        stream.close();
      }
    }
  }
}

■使い方

Thread.initialize(new EnterFrameThreadExecutor());
var url:String = "http://www.hatena.ne.jp/images/logo_portal_hatena.gif";
var file:File = File.applicationDirectory.resolvePath("hatena.gif");
var downloader:DownloadImageThread = new DownloadImageThread(url, file.nativePath);
downloader.start();

■参考サイト

ActionScript Thread Library 1.0 (そうめん) で非同期処理をスマートに

2009-11-17

[][]画像を保存する

こんばんは。

会社の古株なのに下っ端の自分は、昔からいることを理由に古いサービスは

基本的に見ないといけなく、業務負荷がとんでもないことになっています。

(古いサービスは品質がざるなのです。)

さて、会社で画像をダウンロードするアプリを作ったので、そのときのメモ。


■保存の流れ

これは説明するまでもないくらいですが・・・

  1. URLのコンテンツをダウンロードする
  2. ダウンロード完了後、コンテンツを保存する

■画像をダウンロードするURLLoader

URLLoaderを使えば、指定されたURLからコンテンツをダウンロードすることができます。

var urlRequest:URLRequest = new URLRequest(url);
var loader:URLLoader = new URLLoader();
loader.dataFormat = URLLoaderDataFormat.BINARY;
loader.addEventListener(Event.COMPLETE, function(e:Event):void{ onLoadComplete(e, savePath); });
loader.load(urlRequest);

ここでは、画像をダウンロード対象としているのでデータフォーマットはバイナリ形式を指定しています。

実際にダウンロードを行う前に、完了後に呼び出す関数をイベントリスナに登録します。


■イベントリスナに引数を一緒に渡す

今回の場合、保存先を引数として渡したいため、以下のような記述をしています。

function(e:Event):void{ onLoadComplete(e, savePath);

これにより、ダウンロードが完了すると、onLoadCompleteに保存先(savePath)を引数として渡すことができます。

他にも方法がありますが、個人的にしっくりきたのがこの方法でした。

参考:

http://blog.img8.com/archives/2008/04/003758.html


■ロードできたら保存

ダウンロードしたコンテンツは、URLLoaderのdataプロパティに保持されていいます。

ここでは、URLLoader(e.target).dataのようにして参照しています。

ファイルの保存は、FileStreamクラスを利用します。

private function onLoadComplete(e:Event, savePath:String):void
{
  var file:File = new File(savePath);
  var stream:FileStream = new FileStream();
  var byteArr:ByteArray = new ByteArray();
  stream.open(file, FileMode.WRITE);
  stream.writeBytes(URLLoader(e.target).data);
}


全ソースと使い方は続きのページに載せますので、よければお使いください。


ソース

package
{
  import flash.events.Event;
  import flash.filesystem.File;
  import flash.filesystem.FileMode;
  import flash.filesystem.FileStream;
  import flash.net.URLLoader;
  import flash.net.URLLoaderDataFormat;
  import flash.net.URLRequest;
  import flash.utils.ByteArray;

  public class ImageService
  {

    /**
     * コンストラクタ
     */
    public function ImageService()
    {
    }

    /**
     *  URLのコンテンツをダウンロードし保存する
     */
    public function downloadImage(url:String, savePath:String):void
    {
      try {
        var urlRequest:URLRequest = new URLRequest(url);
        var loader:URLLoader = new URLLoader();
        loader.dataFormat = URLLoaderDataFormat.BINARY;
        loader.addEventListener(Event.COMPLETE, function(e:Event):void{ onLoadComplete(e, savePath); });
        loader.load(urlRequest);
      } catch (e:Error) {
        trace(e.getStackTrace());
      }
    }

    /**
     * URLのコンテンツをロード後に呼ばれ、コンテンツを保存する。
     */
    private function onLoadComplete(e:Event, savePath:String):void
    {
      var file:File = new File(savePath);
      var stream:FileStream = new FileStream();
      var byteArr:ByteArray = new ByteArray();
      try {
        stream.open(file, FileMode.WRITE);
        stream.writeBytes(URLLoader(e.target).data);
      } catch (e:Error) {
        trace(e.getStackTrace());
      } finally {
        stream.close();
      }
    }

  }
}

■使い方

var url:String = "http://www.hatena.ne.jp/images/logo_portal_hatena.gif";
var file:File = File.applicationDirectory.resolvePath("hatena.gif");
var imageService:ImageService = new ImageService();
imageService.downloadImage(url, file.nativePath);

2009-02-21

[][]AirRecord

おはようございます!

昨日は会社の新人歓迎会でしたが、飲み会の席でお酒を飲めなかったのは初めての体験でした。

あれはきついですね、、、徹夜状態だったので、飲んだら大変なことになっていたとは思いますが。


さてさて、タイトルのAirRecordなるものを見つけました。

Adobe AIR向けのActiveRecord「AirRecord」

名前から推測できるように、Air用のO/Rマッピングツールです。

RailsのActiveRecordくらい使いやすければいいですね。

ついでに、rakeタスクやfixtureのようなものもあれば完璧ですね。

個人的には、Railsのmigrationは最強だと思ってます!

まだ使っていないので、使ってみての感想などは、検証をしてから報告したいと思います。