Paradigm Shift Design

ISHITOYA Kentaro's blog.

Rails2.1でSWFUploadを使うのまとめ

せっかくなので、まとめておきます。
利用しているものは、次のようになっています。


Rails2.1上でのSWFUploadとrestful_authenticationの組み合わせは、色々設定しないとちゃんと動いてくれないようです。


まず、rails2.1で使う場合の問題点を列挙します。

  • CSRF対策がデフォルトで入っている
  • Flashに対してセッションを送れない
  • session_idに改行が入るようになった
  • 値を返さないと、I/Oエラーになる


というわけで、まず、
#243653 - Pastie
上記URLのスクリプトを、initializersディレクトリにswfupload_fix.rbのような名前で保存します。これは、Session周りのハックをするスクリプトです。


アップロード対象とするコントローラーの先頭に、CSRF対策を無効にして、sessionでクッキーを使わないようにする2行を挿入します。
私のプログラムでは、contentsコントローラーのuploadアクションメソッドをSWFUploadのupload_pathに指定していますので、次のようになります。

session :cookie_only => false, :only => :upload
protect_from_forgery :except => [:upload]


次に、layoutファイルに

<%= javascript_include_tag 'prototype', 'swfupload/swfupload' %>
<%= javascript_include_tag 'prototype', 'swfupload/swfupload.queue' %>
<%= javascript_include_tag 'prototype', 'swfupload/handlers' %>
<%= javascript_include_tag 'prototype', 'swfupload/fileprogress' %>

として、スクリプトを読み込みます。プログレスやqueueを使わない場合は、swfuploadだけでいいです。


ビューは次のようにします。

<script type="text/javascript">
//アップロード成功時のハンドラー
function myUploadSuccess(file, serverData) {
    try {
        eval("var result=" + serverData);
        var progress = new FileProgress(file, this.customSettings.progressTarget);
        progress.setComplete();
        if(result.error){
            progress.setStatus(result.error);
        }else{            
            var content = result.content;
            progress.setStatus("Complete.");
        }
        progress.toggleCancel(false);

    } catch (ex) {
      this.debug(ex);
    }
};


//SWFUploadの設定
var swfu;

window.onload = function() {
  var settings = {
    flash_url : "/swfupload_f9.swf",
    upload_url: "<%= url_for :controller => 'contents', :action => 'upload' %>?_AppName_session=<%= u(session.session_id) %>",	

    file_size_limit : "100 MB",
    file_types : "*.jpg; *.jpeg",
    file_types_description : "Image Files",
    file_upload_limit : 100,
    file_queue_limit : 0,
    custom_settings : {
        progressTarget : "fsUploadProgress",
        cancelButtonId : "btnCancel"
    },
    debug: false,

    // The event handler functions are defined in handlers.js
    file_queued_handler : fileQueued,
    file_queue_error_handler : fileQueueError,
    file_dialog_complete_handler : fileDialogComplete,
    upload_start_handler : uploadStart,
    upload_progress_handler : uploadProgress,
    upload_error_handler : uploadError,
    upload_success_handler : myUploadSuccess,
    upload_complete_handler : uploadComplete,
    queue_complete_handler : queueComplete	// Queue plugin event
  };
  swfu = new SWFUpload(settings);
};
</script>

<!-- アップローダーの設置 -->
<div id="uploader" style="float: left; width=500px">
<input type="button" value="Upload file (Max 100 MB)" onclick="swfu.selectFiles()" style="font-size: 8pt;" />
<input id="btnCancel" type="button" value="Cancel All Uploads" onclick="swfu.cancelQueue();" disabled="disabled" style="font-size: 8pt;" />
<div class="flash" id="fsUploadProgress">
    <!-- This is where the file progress gets shown.  SWFUpload doesn't update the UI directly.
      The Handlers (in handlers.js) process the upload events and make the UI updates -->
</div>

<div id="uploadResult"></div>
</div>

アップロードが成功したとき以外は、handlers.jsで提供されているハンドラを使っています。ここで重要なのは、upload_pathの記述です。

upload_url: "<%= url_for :controller => 'contents', :action => 'upload' %>?_AppName_session=<%= u(session.session_id) %>"

upload_urlの引数として、セッションIDを渡します。引数名は、_AppName_sessionのAppNameをアプリケーション名にするか、自分で設定したものを記述します。またRails2.1ではsession.session_idに改行が入ってしまいますので、uヘルパーを使ってURLエンコードしてください。


後は、コントローラーのアクションメソッドを次のように記述します。

def upload
  uploaded = params[:Filedata]
  render :text => {"message", "Uploaded!"}.to_json
end

これで、Rails2.1上で、restful_authenticationとSWFUploadが正しく動作します。


参考サイト


試行錯誤の痕