2010-10-16
■[Rails] Ajaxファイルアップロードを試してみた(Rails3編) 
以前、記事を書いていたが、
RailsでAjaxファイルアップロードを試してみた - 篳篥日記
Rails 3 になったし、Ajaxファイルアップロードのライブラリも新しくなったので、エントリを新しく書く事にした。
今回も使うのはこちらのライブラリ。
valums’s file-uploader at master - GitHub
などなど、なかなか魅力的。
ということで、今回はこのライブラリを使って最低限のファイルアップロードを目指す。
さくっと作ってみる。
$ rails new ajax_upload $ cd ajax_upload $ rails g model Entry name:string $ r g controller Entries new $ rake db:migrate
ライブラリをダウンロードしたら、fileuploader.js を public/javascripts/ に、fileuploader.css を public/stylesheets/ に、loaging.gif を public/images/ に入れる。
で、ちょっとパッチをあてる(理由は後述。環境によってはこのパッチは不要かも)。
2010.11.2 追記:
パッチあてなくても良いです。fileuploader。jsの中の人に問い合わせて、この問題は解決しています。コントローラの記述を変えてあげればOKなのですが、まだこのエントリに反映できていません。しばらくお待ちください。
--- fileuploader.js.orig 2010-10-16 17:50:36.000000000 +0900 +++ fileuploader.js 2010-10-16 18:01:31.000000000 +0900 @@ -1114,7 +1114,7 @@ qq.UploadHandlerXhr.isSupported = function(){ var input = document.createElement('input'); input.type = 'file'; - + return false; return ( 'multiple' in input && typeof File != "undefined" && @@ -1227,4 +1227,4 @@ this._xhrs[id] = null; } } -}); \ No newline at end of file +});
次に css
--- fileuploader.css.orig 2010-10-16 17:52:32.000000000 +0900 +++ fileuploader.css 2010-10-16 17:52:51.000000000 +0900 @@ -24,8 +24,8 @@ } .qq-upload-file {} -.qq-upload-spinner {display:inline-block; background: url("loading.gif"); width:15px; height:15px; vertical-align:text-bottom;} +.qq-upload-spinner {display:inline-block; background: url("/images/loading.gif"); width:15px; height:15px; vertical-align:text-bottom;} .qq-upload-size,.qq-upload-cancel {font-size:11px;} .qq-upload-failed-text {display:none;} -.qq-upload-fail .qq-upload-failed-text {display:inline;} \ No newline at end of file +.qq-upload-fail .qq-upload-failed-text {display:inline;}
さて、作って行こう。
$ vi config/routes
routes.rbの内容は以下のような感じで、ここでは最低限作ってみる。
AjaxUpload::Application.routes.draw do resources :entries, :only => :new do post :ajax_upload, :on => :new end end
$ rake routes
すると以下のような感じ。
ajax_upload_new_entry POST /entries/new/ajax_upload(.:format) {:action=>"ajax_upload", :controller=>"entries"} new_entry GET /entries/new(.:format) {:action=>"new", :controller=>"entries"}
次にコントローラ。以前のエントリとほぼ同様に。
class EntriesController < ApplicationController def new @entry = Entry.new end def ajax_upload begin _store_upload(params[:qqfile]) rescue render :text => "{ error: #{h($!.message.to_s)} }" else render :text => "{ success: true }" end end private def _store_upload(file) store_dir = "#{Rails.root}/public/store" FileUtils.mkdir_p(store_dir) unless File.exist?(store_dir) dst_file = "#{store_dir}/#{File.basename(file.original_filename)}" if file.respond_to?(:local_path) FileUtils.copy_file(file.local_path, dst_file) elsif file.respond_to?(:read) open(dst_file, "wb") {|f| f.write(file.read) } else raise ArgumentError, "Do not know how to handle #{file.inspect}" end end end
今回もヘルパを作る(app/helpers/uploader_helper.rb)。
ここもとりあえず最低限。
module UploaderHelper def ajax_uploader_script(id, action, options={}) raw script = <<-EOS <script> function createUploader(){ var uploader = new qq.FileUploader({ element: document.getElementById('#{ id }'), action: '#{ action }', params: { authenticity_token: '#{ form_authenticity_token }' } }); } window.onload = createUploader; </script> EOS end end
最後にview (app/views/entries/new.html.erb)。
<%= stylesheet_link_tag 'fileuploader' %> <%= javascript_include_tag 'fileuploader' %> <%= ajax_uploader_script("uploader", ajax_upload_new_entry_path) %> <div id="uploader"> <noscript>Please enable JavaScript</noscript> </div>
これだけ。
サーバを立ち上げて、 /entries/new にアクセスしたら、 "Upload a file" と書かれたエリアをクリックでアップロード開始。
以上で、public/store/ にアップロードされたファイルが入る。
最低限だが、アップロードするだけなら新しいライブラリでも普通に使えた。
あとは、file_columnなりPaperclipなりを組み合わせてやれば良い(もしかしたら、そういうエントリも書くかも)。
当方の環境(MacOS X 10.6.4, Safari 5.0.2, Rails 3.0.0) では、そのまま(つまりxhrを使う方法)だとファイル名しかPOSTされて来なかった。Firefox 3.6.3 でも同様だった。
暫定処置として、qq.UploadHandlerXhr.isSupported が常に falseを返す場合、古いライブラリのように iframeを追加するようになるので、今回はこれで行ってみた。
xhrの場合でもうまく環境によっては、うまく行っている人もいるようなので、ダウンロードしたそのままでも試してみて欲しい(stylesheetへのパッチは必要だが)。
2010.11.2 追記:
パッチあてなくても良いです。fileuploader。jsの中の人に問い合わせて、この問題は解決しています。コントローラの記述を変えてあげればOKなのですが、まだこのエントリに反映できていません。しばらくお待ちください。