いい加減ドラッグ&ドロップでファイルをアップしたい!
あまのです。
社内プロジェクトで久々にRubyとRailsをさわりました。
やっぱりRubyは書きやすくていいですね。
さて今回はドラッグ&ドロップで複数ファイルのアップロードです。
前々から、「そろそろブラウザでもドラッグ&ドロップでファイルアップロードしたい」と思ってたので、今回試しに作ってみました。
参考にしたサイト
篳篥日記
http://d.hatena.ne.jp/hichiriki/20101016
デモ
今回作るサンプルのデモを最初にお見せします。
chromeやSafari, Firefox3.6でUpload a fileに画像ファイルをドラッグ&ドロップしてみてください。
目標
- 最近のブラウザではドラッグ&ドロップでアップロード
- 対応していないブラウザは普通にファイルアップロード
- 複数ファイルに、もちろん対応
- Herokuの無料プランでも使えるようにファイルはサーバ内に置かず、AmazonS3へ保存
1,2,3はfileuploader.jsで、4はpaperclipで。
どんどん便利になっていくなぁと思いました。
環境
ruby 1.8.7
Rails 3.0.1
paperclip 2.3.5
fileuploader
事前準備
Ruby + Rails + Paperclipの環境をまず整えます。
また今回のメインである下記のライブラリをダウンロードします。
valums / file-uploader
http://github.com/valums/file-uploader
ダウンロード後、JSとCSS、ローディングの画像を配置します。
$ mv fileuploader.js public/javascripts/
$ mv fileuploader.css public/stylesheets/
$ mv loading.gif public/images/
loading.gifが正しく呼び出されるようにfileuploader.cssを少し修正しました。
.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;}
Amazon S3の設置と設定
今回はS3に設置しますので、Amazon S3の契約をして、アクセスキーとシークレットキーの取得します。
取得したアクセスキーとシークレットキーはHerokuのConfig Varsに設定します。
$ heroku config:add S3_BUCKET=mybucket S3_KEY=123456... S3_SECRET=abcdef...
モデルの作成
ほぼpaperclipのサンプルのままですが、Amazon S3など環境に合うように設定しました。
app/models/users.rb
class User < ActiveRecord::Base
# original_filename: config/initializers/paperclip.rb
has_attached_file :avatar,
:storage => :s3,
:s3_credentials => {
:access_key_id => ENV['S3_KEY'],
:secret_access_key => ENV['S3_SECRET']
},
:bucket => ENV['S3_BUCKET'],
:styles => { :medium => "300x300>", :thumb => "100x100>" },
:path => "/dev/ajaxupload-demo/:id/:style_:original_filename",
:url => "/dev/ajaxupload-demo/:id/:style_:original_filename"
end
class AddAvatarColumsToUser < ActiveRecord::Migration
def self.up
add_column :users, :avatar_file_name, :string
add_column :users, :avatar_content_type, :string
add_column :users, :avatar_file_size, :integer
add_column :users, :avatar_updated_at, :datetime
end
def self.down
remove_column :users, :avatar_file_name
remove_column :users, :avatar_content_type
remove_column :users, :avatar_file_size
remove_column :users, :avatar_updated_at
end
end
ヘルパーの作成
ヘルパーメソッドを定義します。
注意点として、authenticity_tokenを設定する必要があります。
app/helpers/upload_helper.rb
module UploaderHelper
def ajax_uploader_script(id, action, options={})
raw script = <<-EOS
EOS
end
end
ビューの作成
app/views/entries/new.html.erb
<%= stylesheet_link_tag 'fileuploader' %>
<%= javascript_include_tag 'fileuploader' %>
<%= ajax_uploader_script("file-uploader", ajax_upload_new_entry_path) %>
<div id="file-uploader">
<noscript>Please enable JavaScript</noscript>
</div>
コントローラーの作成
今回の肝であるコントローラーの作成です。
最初ドラッグ&ドロップした時に Rails 側でうまく受け取ることができず、結構はまりました。
fileuploader.jsはドラッグ&ドロップでファイルをアップロードする時、XMLHttpRequestでファイルを送るのですが、Content-typeはapplication/octet-streamで送られます。multipart/form-dataで送られていないことに注意です。私はここではまりました…
またファイル一つ一つ、それぞれXMLHttpRequestで送られてきます。
Rails側では、XMLHttpRequestが使われた時に、リクエストデータをそのままファイルデータとして保存します。XMLHttpRequestで送られなかった場合は、通常のmultipart/form-dataで送られるため、普段のファイルアップロードと同じようにparams[:qqfile]をFileオブジェクトとして扱います。
Tempfileを使っているのは、Amazon S3に配置するために一時ファイルを作成して、paperclipに渡します。これでHerokuでも使えます。
fileuploader.js 側でアップロードが成功したかどうかを次の JSON を受け取れるかで判定しているため、受け取りに成功したら返すようにします。
{ success: true }
失敗した場合は、下記のように返します。
{ error: 'エラーメッセージ' }
app/controllers/entries_controller.rb
class EntriesController < ApplicationController
def new
@entry = Entry.new
@users = User.limit(5).order('id DESC')
end
def ajax_upload
begin
_store_upload(params[:qqfile])
rescue
render :json => { :error => $!.message.to_s }
else
render :json => { :success => true }
end
end
private
def _store_upload(file)
if request.xhr?
if request.body.length == request.headers['CONTENT_LENGTH'].to_i
file_data = request.body.read
file_name = file
end
else
if file.instance_of?(File)
file_data = file.read
file_name = file.original_filename
end
end
raise "upload failure" unless file_data
tf = Tempfile.new(file_name)
tf << file_data
@user = User.new
@user.avatar = tf
@user.avatar_file_name = file_name
@user.save
tf.close!
end
end
もう一つ注意する点がありました。
Tempfile を使っているため、ファイル名が「元のファイル名pid.n」みたいになります。
それを paperclip に渡しているため、paperclip がそのファイル名で保存してしまいます。
そのため、事前に file_name という変数にファイル名を入れて、avatar_file_nameに設定します。
これで大丈夫かというと、実はダメで、ここでまたはまりました。
:path => "/dev/ajaxupload-demo/:id/:style_:original_filename",
users.rb で上記のようにoriginal_filename と書いてありますが、これは paperclip で提供されるシンボルではなく、config/initializers/paperclip.rbを作成することで、original_filenameが呼ばれたら、avatar_file_name を返すようにしています。
config/initializers/paperclip.rb
Paperclip.interpolates :original_filename do |attachment, style|
attachment.instance.avatar_file_name
end
これで完了です。
サンプルコード
githubにサンプルとしてファイルを置いてあります。
Herokuで動作するようにしてあります。
http://github.com/amano/ajaxupload-demo
取得後、次の作業を行ってください。
- HerokuのConfig VarsにS3のアクセスキーなどを設定
- app/models/users.rbをS3に合うように修正
Herokuに設置して、http://{appname}.heroku.comにアクセスすれば見えると思います。
Herokuに関しては以下のサイトがわかりやすかったので参考にしてみてください。
Ruby版PaaSの”Heroku”で無料Railsホスティング環境を手に入れよう
http://kuranuki.sonicgarden.jp/2009/05/rubypaasherokurails.html
fileuploader.jsは便利そうなので、良い使い方があればぜひ教えてください。
このエントリーに対するコメント
-
とても参考になりました。
Rails で画像等の管理を行いたいと思い、こちらの記事にたどり着きました。 git も理解の助けになりました。
ありがとうございます。
2011年07月02日, 4:42 AM
-
ありがとうございます。
何か不明な点や不具合などありましたら、ご連絡ください。2011年07月04日, 12:26 PM
-
ちょうど探していたモノでした。難しそうですが、使えるように頑張りたいです。
2011年09月13日, 7:58 PM
-
I must say I am very impressed with how you and your site effectively, the messages are very informative. Really captured the attention of many apparently to go!
2011年11月04日, 8:07 PM
-
これを使ってIEでajaxのアップロードを行いたいのですが、ドラッグアンドドロップの機能を付けなければIEで使用可能でしょうか?
どのように書けばいいか教えていただけないでしょうか?開発環境
Rails 3.0.7
ruby 1.9.2p2902011年12月07日, 4:57 PM
-
すみません!
私もIEでの利用はよく調査しておりません。2011年12月07日, 5:04 PM
-
たびたびすみません。
ajaxを使ってファイルのアップロードしたいのですが、上のコード参考にrails3で実装させていただきました。
ドラッグアンドドロップの機能はなしでいいのですが、IEで動かしたい場合はどのように書けばいいのか教えていただけないでしょうか?
お手数ではございますがよろしくお願いします。2011年12月07日, 5:14 PM
-
ソフトウェアは、ドラッグアンドドロップのようにユーザーフレンドリーな相間を提供していますがはるかに人気の高い実装が困難であるソフトウェアよりもなります
2012年05月11日, 3:05 PM
-
ですが、IEで動かしたい場合はどのように書けばいいのか教えていただけないでしょうか?
2013年01月03日, 9:11 PM
-
ちらの記事にたどり着きました。 git も理解の助けになりました。
2013年01月18日, 7:25 PM
日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)
- トラックバック
-
- @blog.justoneplanet.info2010/11/02, 11:38 AM
もっとFile APIを使ってXMLHttpRequestと組み合わせてみる
XMLHttpRequest Level2ではバイナリデータもアップロードできるようになった!ヽ(=´▽`=)ノ ■ソース 基本的には前回と同じコードを利用している。 <p ondragstart="dragstart(event)" ondragenter=…
-
- [Rails3] ドラッグ&ドロップでファイルアップロード | KRAY Inc | Ruby On Rails ニュース2010/11/03, 3:01 PM
[…] Ruby On Railsに関する、はてなブックマーク新着情報です。 [Rails3] ドラッグ&ドロップでファイルアップロード | KRAY Inc […]
-
- fleximage と fileuploader.js | 大縄亮のブログ2011/05/26, 12:35 AM
「いいね!」で応援よろしくお願いします!