RubyでDropboxAPIを使ってFileアップロード

手順その1.アプリ登録

 まずはこちらからアプリを登録。プラットフォームとかいろいろあるけどとりあえずほっとき、重要なのはkeyとsecret。コレをどこかにメモしときます。
 そもそもDropboxに登録してないぜ!って場合はそこから…

手順その2.アクセストークン、シークレット取得

 こちらドキュメンテーションがあるので読んでみる(英語か…)。とりあえず「/token」のところかな?リクエストメソッドはGet・Postどちらでもいいみたいだけど、まあ普通Postでしょう。必須パラメータはemailpasswordとある。というわけでこの2つをPostのbodyとしてリクエストを送ってみると…エラー!そもそもkeysecret使って無いじゃないか!
もうちょっとよく読んでみるとなにやら英語で書いてあるけど具体的にどうすればよいのかよくわかりません…とりあえずbodyにoauth_consumer_keyoauth_consumer_secretという名前でくっつけてリクエストを投げてみると…なんと成功!まじかww
(ここでのbody: email=xxxx&password=xxxx&oauth_consumer_key=xxxx&oauth_consumer_secret=xxxx)
さすがにどうかと思うのでヘッダに付けて加えてbodyから除いてみた(Authorization: OAuth oauth_consumer_key="xxxx", oauth_consumer_secret="xxxx")。これでも成功。なんでもありだなw
とりあえずこれでアクセストークンとアクセストークンシークレット取得成功!?

手順その3.ファイルアップロード

ここが今回の目標。ドキュメンテーション「/files」というところを見てみる。URLが2つあるけど?とりあえず上のやつを使ってみよう。そしてまた情報少ないな…
Fileのアップロードってどうやるんだっけ?と思いつつ昔作ったプログラムを見ながら改造して…とりあえずアップする部分はこんな感じでいけるかな?

uri=URI.parse(requestURL)
 
https=Net::HTTP.new(uri.host,uri.port)
https.use_ssl=true
https.verify_mode=OpenSSL::SSL::VERIFY_NONE

req=Net::HTTP::Post.new(uri.path)
req["authorization"]=header
req["content-type"] ="multipart/form-data; boundary=#{boundary}"
 
body=Array.new
body << "--#{boundary}"
body << "content-disposition: form-data; name=\"file\"; filename=\"sample.html\""
body << ""
body << file
body << "--#{boundary}--"
body << ""
req.body=body.join("\r\n")
 
return https.start.request(req)

でやってみたらエラー!timestampがないとか言ってやがる…。てことはTwitterと同じやり方でやるのか??と思ってこれまた昔作ったTwitterプログラムを引っ張り出す。で適当に改造して作ったのが以下。

require "net/https"
require "base64"
require "openssl"
require "erb"

module Dropbox
  module Signature
    CONSUMER_KEY="xxxx"
    CONSUMER_SECRET="xxxx"
    SIGNATURE_METHOD="HMAC-SHA1"	
    OAUTH_VERSION="1.0"
    
    def self.makeHeader(str)
      ret=Array.new
      str.split("&").each{|elem|
        ret << "#{elem.split("=")[0]}=\"#{elem.split("=")[1]}\""
      }
      return ret.join(",")
    end
    
    def self.getTimeStamp
      return Time.now.tv_sec
    end
    
    def self.getNonce
      ret=Array.new
      size=8  
      for i in 0..size-1
        case rand(3)
        when 0 #a-z
          ret[i]=(97+rand(26)).chr
        when 1 #A-Z
          ret[i]=(65+rand(26)).chr     
        when 2 #0-9
          ret[i]=(48+rand(10)).chr;
        end
      end
      return ret.join("")
    end
    
    def self.getSignature(signatureBaseString, keyString)
      digest=OpenSSL::HMAC.digest(OpenSSL::Digest::SHA1.new,keyString,signatureBaseString)
      signature=ERB::Util.u((Base64.encode64(digest)).chomp)
      return signature
    end
    
    def self.encode(str)
      return (ERB::Util.u(str)).gsub("%7E","~")
    end
  end
  
  module File
    def self.upLoad(accessToken,accessTokenSecret,filestr,path)
      requestURL="https://api-content.dropbox.com/0/files/dropbox/#{path}"

      requestParameters="file=sample.html&oauth_consumer_key=#{Signature::CONSUMER_KEY}
&oauth_nonce=#{Signature::getNonce()}&oauth_signature_method=#{Signature::SIGNATURE_METHOD}
&oauth_timestamp=#{Signature::getTimeStamp()}&
oauth_token=#{accessToken}&oauth_version=#{Signature::OAUTH_VERSION}"
      signatureBaseString="POST&#{Signature::encode(requestURL)}&#{Signature::encode(requestParameters)}"
      keyString="#{Signature::CONSUMER_SECRET}&#{accessTokenSecret}"
      signature=Signature::getSignature(signatureBaseString,keyString)
      
      header="OAuth #{Signature::makeHeader(requestParameters)},oauth_signature=\"#{signature}\""
      
      uri=URI.parse(requestURL)
      
      https=Net::HTTP.new(uri.host,uri.port)
      https.use_ssl=true
      https.verify_mode=OpenSSL::SSL::VERIFY_NONE
      
      boundary=Time.now.strftime("%Y%m%d_%H%M%S_#{$$}")
      req=Net::HTTP::Post.new(uri.path)
      req["authorization"]=header
      req["content-type"] ="multipart/form-data; boundary=#{boundary}"
      
      body=Array.new
      body << "--#{boundary}"
      body << "content-disposition: form-data; name=\"file\"; filename=\"sample.html\""
      body << ""
      body << filestr
      body << "--#{boundary}--"
      body << ""
      
      req.body=body.join("\r\n")
      return https.start.request(req)
    end
  end
end

token="xxxx"
secret="xxxx"
path="xxxx"
file="watashihabaka"

res=Dropbox::File.upLoad(token, secret, file, path)
puts res.code  #=>200
puts res.body  #=>{"result": "winner!"}

これでアップロード成功。「winner!」ってなんだw ていうか各プラットフォーム毎にSDK?も提供されてるしそれを使えという話も…
ということで第1回DropboxAPIでファイルアップロードでした^^

しかし汚いソース載せるの恥ずかしいな…w改善した方がいいところとかバシバシ教えてください!

ちなみにこれは開発用のものであって正式に使うためには「公開申請」とやらをしなければならないらしいです(開発用は自分のアカウントにしかアクセスできない、回数制限がかかる等の問題あり)