3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

AtomAPIを使用してサクっとはてなフォトライフへ画像をアップロードする

Last updated at Posted at 2016-06-08

TL;DR

ローカルからサクっとはてなフォトライフへ画像をアップロードするためのrubyスクリプトです。
ユーザ名とAPIキーを使用したWSSE認証方式を用います。
下記コードにて、ENVにユーザ名とAPIキーをセットして、ファイルパスを引数で渡せばアップロード完了です。
https://gist.github.com/chanibarin/4c95dff8858e25cda588115e8c0634ad

はじめに

ローカルからサクっとはてなへ写真をアップロードしたかったのですが、最近のサンプルが全然なく、サクっと動きそうなものが見当たらなかったので調査して作りました。
はてなの資料を愚直に実装するのみです。
http://developer.hatena.ne.jp/ja/documents/fotolife/apis/atom
http://developer.hatena.ne.jp/ja/documents/auth/apis/wsse

WSSE認証

現在(2016/6/9)のはてなのAtomAPIはユーザ名とAPIキーによるWSSE認証もしくはOAuth認証のどちらかでアクセスするのですが、ローカルからサクっとアップロードしたいだけなのでWSSE認証を選択しました。(OAuthの方が最近のサンプルが多そうなので僕自身は結果的にはサクっと終わらなかったという)
WSSE認証はリクエストヘッダにX-WSSEを送ります

X-WSSE: UsernameToken Username="hatena", PasswordDigest="ZCNaK2jrXr4+zsCaYK/YLUxImZU=", Nonce="Uh95NQlviNpJQR1MmML+zq6pFxE=", Created="2005-01-18T03:20:15Z"

これらに入れる値ですが、

Username
はてなID
Nonce
HTTPリクエスト毎に生成したセキュリティ・トークン
Created
Nonceが作成された日時をISO-8601表記で記述したもの
PasswordDigest
Nonce, Created, APIキーを文字列連結しSHA1アルゴリズムでダイジェスト化して生成されたオクテット列を、Base64エンコードした文字列。APIキーは投稿メールアドレスの"@"や"+"以前の文字列になります。はてなブログの管理画面でも確認できます。

ということでこれらをそのまま用意すると良さそうなのですが…ハマりポイントが存在します。

ハマりポイント

NonceとPasswordDigestの一部として入るNonceは同一ではありません…
PasswordDigestに付加するNonceをbase64エンコードしたものがNonceになります。
PasswordDigest = "AAAA"(Nonce) + Created + API_KEY
とすると、
Nonce = Base64.encode64("AAAA")
となります。

APIキーの取得

リンクから飛んでもらえると取得できると思いますが、念のためスクショを貼っておきます。

ブログ設定の詳細設定を選択し、

スクリーンショット 2016-06-09 1.51.53.png

下の方に行くとAtomPubという項があるので、そこに記載されているAPIキーの値になります。ちなみに、ルートエンドポイントにはてなIDが書かれているので(USERNAME)、こちらからはてなIDも確認できます。

スクリーンショット 2016-06-09 1.52.58.png

Nonce

ノンスはランダム値を適当に作成して下さい。

base64nonce = SecureRandom.base64(64) # PasswordDigestに使用
nonce = Base64.decode64(base64nonce)

Created

こちらはISO-8601表記で作成してください。

Time.now.utc.iso8601

トークン生成

以上を踏まえて、下記コードにてトークンを生成出来ます。

require 'time'
require 'securerandom'
require 'base64'

timestamp = Time.now.utc.iso8601

base64nonce = SecureRandom.base64(64)
nonce = Base64.decode64(base64nonce)
token = nonce + timestamp + API_KEY
password_digest = Base64.encode64(Digest::SHA1.digest(token)).chomp!

wsse = "UsernameToken Username=\"#{USERNAME}\", PasswordDigest=\"#{password_digest}\", Nonce=\"#{base64nonce}\", Created=\"#{timestamp}\""

アップロード処理

ここまで来たら後は普通にHTTPでポストするだけです。

POST /atom/post

Sample /9j/2wCEAAQDAwQDAw.../9n/AA== ```

仕様書には書いてありませんが…、ホストは自アカウントのurlとかではなく、フォトライフのurlになります。
f.hatena.ne.jp

画像ファイルの読み込み

サンプルはゴロゴロ転がってますが一応書いておきます。

require 'mime/types'
require 'base64'

file_path = ARGV[0] # 第一引数を画像ファイルパスとする
content = Base64.encode64(open(file_path).read) # 画像ファイルをロードしてBase64エンコード
mime_type = MIME::Types.type_for(file_path).first # MIMEタイプ取得
title = File.basename(file_path, '.*') # ファイルタイトル取得

アップロード

お待たせしました!アップロードしましょう!

header = {'X-WSSE' => wsse} # ヘッダにWSSEトークンを渡す
entry = <<-"ENTRY" # エントリデータ生成
<entry xmlns="http://purl.org/atom/ns#">
  <title>#{title}</title>
  <content mode="base64" type="#{mime_type}">
    #{content}
  </content>
</entry>
ENTRY


client = Net::HTTP.new('f.hatena.ne.jp', 80)
response = client.post('/atom/post', entry, header)
puts response

アップロード確認

#<Net::HTTPCreated:0xXXXXX> が出力されることを確認したら、フォトライフを見てみましょう。

スクリーンショット 2016-06-09 2.13.44.png

アップロードされていたら成功です!
お疲れ様でした。

実行コード

こちらから
https://gist.github.com/chanibarin/4c95dff8858e25cda588115e8c0634ad

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?