sinatra + html5_canvas + jquery 画像ドラッグドロップでファイル保存

こんにちは。麺処まつば副店長です。風が凄まじいですね(台風)
ここ暫く更新せずにいたら、ついに店長の大目玉をくらいました。
台風と店長の大目玉のダブルパンチ。恐ろしや……。

さて今日は、sinatra + HTML5 + jquerycanvas にドラッグドロップされた画像を
サーバに送信ファイル保存しつつ表示というのをやってみようと思います。
どれだけ需要があるか分かりませんが(笑)

手順はこんなカンジです。
1.views/index.haml
2.public/javascripts/dragdrop.js
3.app.rb

それでは作っていきましょう。

views/index.haml

副店長の悪いクセなんですが、まず見た目から入ります。
canvas をひとつだけおいたファイルを用意します。

  1 !!! XML
  2 !!!
  3 
  4 %html{:lang => "ja"}
  5   %head
  6     %meta{ :content=>"text/html", :charset=>"utf-8" }
  7     %title 画像
  8     %link{ :rel => "stylesheet", :href =>"/style.css" }
  9     %script{ :src => "javascript/jquery-1.6.2.min.js" }
 10     %script{ :src => "javascript/dragdrop.js" }
 11   %body
 12     #main
 13     %canvas{ :id=> "img_canvas",
 14              :width => "500", :height => "500",
 15              :ondragover => "onDragOver(event)", :ondrop => "onDrop(event)",
 16              :style => "border: medium dotted #666;"}

9、10行目で、自作する js ファイルと、使用する jquery を指定しています。
13〜16行目で、canvas を設置しています。
ondragover / ondrop で、自作 js の関数を呼びます。
それから、canvas の width と height は、
スタイルシートで指定しないほうがいいみたいです。
こないだ参加したHTML5勉強会の講師の先生が
スタイルシートで書くと何故かうまくいかないって仰ってました。
(そして自分では試してないので、どううまくいかないのかは良く分かりません(笑))

public/javascript/dragdrop.js

上記で書いた canvas に画像がドラッグドロップされたときに動く
javascript をかいていきます。

  1 function onDrop(event){
  2   // drop されたファイルの取得
  3   var f = event.dataTransfer.files[0];
  4 
  5   // drop されたファイルが画像ファイルの場合の処理
  6   if(/^image/.test(f.type)){
  7     // 画像ファイルを読み込むための準備 
  8     var img = document.createElement('img');
  9     var fr = new FileReader();
 10     
 11     fr.onload = function(){
 12       img.src = fr.result;
 13       img.onload = function() {
 14         // jquery ajax を使ってPOST
 15         new $.ajax({ 
 16           url: "/post_img",
 17           type: "POST", 
 18           data: {file: img.src},
 19           success: function(){
 20                      // canvas に画像描画
 21                      canvas = document.getElementById("img_canvas");
 22                      context = canvas.getContext("2d");
 23                      canvas.width = img.width;
 24                      canvas.height = img.height;
 25                      context.drawImage(img, 0, 0);
 26                    },
 27           error: function(){ alert("ごめん失敗した。");},
 28           complete: function(){ alert("保存した!"); }
 29         });
 30       }
 31     }
 32     fr.readAsDataURL(f);
 33   }
 34   
 35   // ブラウザがファイル自体を表示するのを防止
 36   event.preventDefault();
 37 } // end funciton [openDrop]
 38 
 39 function onDragOver(event){
 40   event.preventDefault();
 41 } // end function [onDragOver]

こんなカンジです。
15行目から、ajax を使って画像をPOSTしています。
成功すれば、canvas に読み込んだ画像を表示するようにしています。

app.rb

sinatraruby のファイルです。
画面表示と送信された画像を受けてファイル保存なぞします。

 1 #coding:utf-8
  2 
  3 require 'rubygems'
  4 require 'sinatra'
  5 require 'haml'
  6 
  7 configure :production do
  8 end
  9 
 10 get '/' do
 11   set :haml, :format => :html5
 12   haml :index
 13 end # end [get /]
 14 
 15 post '/post_img' do
 16   # 画像ファイルの取得
 17   img_array = params[:file].split(/\s*,\s*/)
 18   
 19   # 拡張子
 20   ext = ".dat"
 21   case img_array[0]
 22   when "data:image/jpeg;base64"
 23     ext = ".jpg"
 24   when "data:image/gif;base64"
 25     ext = ".gif"
 26   when "data:image/png;base64"
 27     ext = ".png"
 28   end
 29   
 30   # 画像ファイル保存
 31   f = File.open("tmp/image#{ext}",'w')
 32   f.puts img_array[1].unpack('m')[0]
 33   f.close
 34   return ""
 35 end
 36 
 37 get '/style.css' do
 38   content_type 'text/css', :charset => 'utf-8'
 39   sass :style
 40 end

10〜13行目で、/ へのGETの定義
15〜35行目で、post された画像を保存する処理を書いています。
ajaxからどんなデータが渡ってくるかなって思ったら、
base64エンコードされた文字列でした。
こんな感じの→「画像情報, エンコードされた文字列」

"data:image/jpeg;base64", "/9j/4AAQSkZJRgAB…

カンマ区切りだったんで配列化して、0番目の情報から画像情報を取得、
ファイル保存時の拡張子を指定しています(19〜28行目)
30〜33行目の間でファイルを保存しています。
終わりです。
※あ。「tmp」ディレクトリ作っといてくださいね。

では、さっそくできた画面内の canvas 領域に画像をドラッグドロップしてみましょう。
canvas に画像が表示されますか?→OK
サーバに画像は保存されますか?→OK

できました。
次回はコレをストレージに保存してみるところをやってみようと思います。

お世話になりますm(_ _)m
HTML5基礎