Hatena::Diary

篳篥日記 このページをアンテナに追加 RSSフィード

2010-10-16

[] Ajaxファイルアップロードを試してみた(Rails3編) 23:40  Ajaxファイルアップロードを試してみた(Rails3編) - 篳篥日記 のブックマークコメント

以前、記事を書いていたが、

RailsでAjaxファイルアップロードを試してみた - 篳篥日記

Rails 3 になったし、Ajaxファイルアップロードライブラリも新しくなったので、エントリを新しく書く事にした。


今回も使うのはこちらのライブラリ

valums’s file-uploader at master - GitHub

などなど、なかなか魅力的。

以前はPrototype版、jQuery版に分かれていた。


ということで、今回はこのライブラリを使って最低限のファイルアップロードを目指す。



さくっと作ってみる。

$ 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 へのパッチ

--- 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なりを組み合わせてやれば良い(もしかしたら、そういうエントリも書くかも)。


fileuploader.jsパッチをあてている理由

当方の環境(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なのですが、まだこのエントリに反映できていません。しばらくお待ちください。