読者です 読者をやめる 読者になる 読者になる

Paradigm Shift Design

ISHITOYA Kentaro's blog.

Rails2.1でSWFUploadを使う

この記事は、試行錯誤の経過です。まとめは、Rails2.1でSWFUploadを使うのまとめ - Paradigm Shift Designをご覧ください。



swfupload -


JavaScript & Flash Upload Library - Google Project Hosting
Rails 2.1に導入してみた記録。
最初、id:takihiroさんがまとめてくださっている、 Rails で SWFUpload を使う - takihiroの日記を読みつつ設定してみた。


コードはサンプルからコピーしてきたのでこんな感じ

<h1>複数ファイルのアップロード</h1>

<script type="text/javascript">
var swfu;

window.onload = function() {
  var settings = {
    flash_url : "/swfupload_f9.swf",
    upload_url: "<%= url_for :controller => 'contents', :action => 'upload' %>",
    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: true,

    // 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 : uploadSuccess,
    upload_complete_handler : uploadComplete,
    queue_complete_handler : queueComplete	// Queue plugin event
  };
  swfu = new SWFUpload(settings);
};
</script>

<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>

でコントローラーのアクションメソッドは、

def upload
  p params
end

てな感じ。もちろん、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とProgressを使うためのファイルをincludeする行を追加してあります。
この状態で、アップロードしてみると…SWFUploadのボタンを押すと

Error #2044: ハンドルされていない IOErrorEvent : text=Error #2038: ファイル I/O エラー。

とかいわれ、WEBrickのログには、

127.0.0.1 - - [13/Aug/2008:10:38:11 東京 (標準時)] "POST /contents/upload HTTP/1.1" 422 19900

- -> /contents/upload

とか表示される。


これはどうも、2.1?から標準になったCSRF対策のおかげらしい。
MuraTaka 速記メモ / 2008-06-13を参考に、コントローラーの冒頭に

protect_from_forgery :except => [:upload]

を追加する。ただ、上記サイトの方もおっしゃっているが、これで正しいのかは知りません。
で、よし、動くだろうと勇んでボタンを押したところ、また

Error #2044: ハンドルされていない IOErrorEvent : text=Error #2038: ファイル I/O エラー。

とか…orz


がしかし、WEBlickのログを読むに

127.0.0.1 - - [13/Aug/2008:11:45:32 東京 (標準時)] "POST /contents/upload HTTP/1.1" 500 661

- -> /contents/upload

{"Filename"=>"IMG_0182.jpg", "action"=>"upload", "Upload"=>"Submit Query", "controller"=>"contents", "Filedata"=>#<File:C:/DOCUME~1/kent/LOCALS~1/Temp/CGI20080813-6848-bb3fd0-0>}

と、アクションメソッドは呼ばれている。なぜに500…とか思いつつ、色々探してみる。
には、

Flash has problems dealing with Rails sessions so a couple of quick fixes are needed. The first is to hack the sessions class. The fix can be found here.

より引用

とあって、Railsとやり取りするには,session classをhackする必要があるよ。と書いてある。お!と思って、やってみる*1
caboo.seをinitializersの中にswfupload_fix.rbとして保存し、コントローラーの先頭に

session :cookie_only => false, :only => :create

と追記して、これで大丈夫だろう!とボタンを押すも…状況変わらず。


で、色々悩んだ挙句、上記サイトのサンプルコードを見ると、ブラウザに何か値を返している…

def upload
  p params
  render :text => "success!"
end

とか、適当にやってみた。…動くじゃないか!
というわけで、swfupload_fix.rbとsessionを消してもきちんと動作しました。


結論としては、SWFUploadをRails2.1で動作させるためには、

  • CSRFの対策をアップロード用のアクションメソッドで無効にする
  • 必ず値を返すようにする

の二つが必要なようです。


まとめました。Rails2.1でSWFUploadを使うのまとめ - Paradigm Shift Design

*1:結論的には不要です