akishin999の日記

調べた事などを書いて行きます。

Flash の画面をキャプチャして Rails で保存する

久しぶりに FlashDevelop をインストールして ActionScript で遊んでみました。
作ったのは簡単なペイントツールで、描いた絵をサーバ側の Rails で保存するというもの。


調べてみると ActionScript で画面をファイルとして保存するには BitmapData に画面の内容を描画して ByteArray に変換、その後そのバイト列をそのままサーバに送信してサーバ側でファイル保存、という流れでいいみたいですね。


サーバ側への送信前に BitmapData を JPEG 形式にエンコードするために、以下のライブラリに含まれている JPGEncoder クラスを使用しました。


as3corelib - Google Code
http://code.google.com/p/as3corelib/


FlashDevelop では Project ペインの lib フォルダ上で右クリック - 「Add」 - 「Existing File」 でアーカイブに含まれる「as3corelib.swc」 ファイルを選択して読み込みます。
その後読み込んだ as3corelib.swc ファイルを右クリック - 「Add To Library」とすれば使えるようになります。


ActionScript 側での保存処理部分のみ抜粋すると以下のようになります。

var bitmapData:BitmapData = new BitmapData(stage.stageWidth, stage.stageHeight);
// 作ったビットマップデータにキャンバス用の Sprite を指定し描画する
bitmapData.draw(canvas);
// JPEG 形式のバイト列にエンコード
var jpgEncoder:JPGEncoder = new JPGEncoder(80);
var byteArray:ByteArray = jpgEncoder.encode(bitmapData);

var urlRequest:URLRequest = new URLRequest();			
urlRequest.url = "http://localhost:3000/example/upload";
urlRequest.contentType = "application/octet-stream";
urlRequest.method = URLRequestMethod.POST;
urlRequest.data = byteArray;

var urlLoader:URLLoader = new URLLoader();
urlLoader.addEventListener(Event.COMPLETE, onCompleteHandler, false, 0, false);
urlLoader.load(urlRequest);


次はサーバサイドの Rails 側です。

今まで Rails で「application/octet-stream」の POST データを扱った事がなかったので、どうするのかと思ったのですが、調べてみるとちゃんと生の Request 送信データを取得する方法が用意されていました。


Class: ActionController::Request
http://api.rubyonrails.org/classes/ActionController/Request.html


ここを読んだ感じでは、Request#body() か Request#raw_post() を使えば良さそうです。

  • body()

body()

The request body is an IO input stream. If the RAW_POST_DATA environment variable is already set, wrap it in a StringIO.

  • raw_post()

raw_post()

Read the request \body. This is useful for web services that need to work with raw requests directly.


StringIO でラップされている body() を使う方が楽そうなので、以下のような感じで実装してみました。

class ExampleController < ApplicationController
  UPLOAD_PATH = RAILS_ROOT + "/files"
  RANDOM_ARRAY = ('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a

  skip_before_filter :verify_authenticity_token ,:only => [:upload]
  
  def upload
    filename = "#{generate_random_string}.jpg"
    filepath = File.join(UPLOAD_PATH, filename)
    File.open(filepath, 'wb') do |f|
      f.write(request.body.read)
    end
    render :text => filename
  end

private

  # ランダムな文字列を作成する。
  def generate_random_string(length = 8)
    Array.new(length) { RANDOM_ARRAY[rand(RANDOM_ARRAY.size)] }.join
  end
end


ランダムに作成した適当なファイル名で、request.body の内容を保存しています。
「skip_before_filter :verify_authenticity_token」は Flash からそのまま POST すると発生する「InvalidAuthenticityToken」用に入れました。


これで Flash 上で描いた画像が Rails 側にちゃんと保存されました。

開発環境

ソフトウェア バージョン
JDK 1.6.0_14
Flex SDK 3.4.0.6955
FlashDevelop 3.0.2-RTM
as3corelib .92.1
Ruby 1.8.6
Rails 2.2.2