2011/06/24


Go言語フリークの皆さんこんばんわ。
テンポラリ的にキャプチャ画像をアップロードする際にはgyazoが一般的ですよね。僕もよく利用しています。
今日はこのgyazoに画像をアップロードするクライアントと、サーバを書きました。
mattn/gyago - GitHub

Gyazo application written in go

https://github.com/mattn/gyago
Gyago

Gyazo on AppEngine

http://gyazo.compile-error.net/
コマンドラインで動くクライアントと、GoogleAppEngineで動くサーバです。クライアントはキャプチャ動作はしませんが、引数で与えたpngファイルをgyazoサーバにアップロード出来ます。またサーバはdatastoreを使って画像データをblobに格納する事でgyazoサーバを実現しています。

タイトルページはGoogle WebFont APIを使ってなんなーくカッコいい感じにしてみました。ほんとになんとなーくです。
Google Web Fonts

Google Web Fonts lets you browse all the fonts available via theGoogle Web Fonts API. All fonts in t...

http://www.google.com/webfonts
GoogleAppEngineの無料枠なので、すぐMax Quotaになっちゃうかも知れませんが、良かったら遊んでみて下さい。
なお、クライアントはデフォルトではgyazo.comにアップロードしちゃうので # gyago -e=http://gyazo.compile-error.net/ imagefile.png
という感じにアップロード先のエンドポイントを指定してあげて下さい。 ちなみに、sendさんがsinatraで書いたgyazoサーバにアップロードしたい場合は # gyago -e=http://gyazo.send.sh/ imagefile.png
と指定すれば動きますが、現状のGoはmultipartの処理で、先頭に空白行が1行入る動作になっていて、rack-1.2と相性が悪い(先頭に空白行があるとマッチしない)ので、golang-devにパッチを投げてあります。
と書いた所で再度見たら、もうsubmitされてましたので、hg pullしてtipsにすると入ってます。

ちなみに、rack-1.3では先頭に空白行が入っていても正しく読める様な修正が入っている模様です。

Posted at by



2011/06/09


Google App EngineへのGo言語アップロード権(Trusted Tester for the golang runtime for appengine)を得たので、Webアプリを作り始めたら皆が必ず作るという「おうっふー」をGo言語に移植してみた。
なお、「おうっふー」とはログイン画面のみを持ち、ログインすると有無を言わさず「おうっふー」という単語をログインアカウントにポストするWebアプリケーションの事。知ってない奴はWeb界のモグリだ!って隣の人が言ってた。
以下、Go言語のコードを解説してみる。 ログイン画面からログインハンドラに要求が行われると、OAuth1.0でリクエストトークンがTwitterに要求され、得たテンポラリトークンで認証画面に遷移します。 http.HandleFunc("/login"func(w http.ResponseWriter, r *http.Request) {
    client := urlfetch.Client(appengine.NewContext(r))
    tmp, err := creds.request(client)
    if err != nil {
        http.Error(w, err.String(), 500)
        return
    }
    url := fmt.Sprintf("%s?oauth_token=%s&oauth_callback=%s", authURI, http.URLEscape(tmp.Key), http.URLEscape(callbackURI))
    w.Header().Set("Set-Cookie""tmp="+http.URLEscape(tmp.Key)+"/"+http.URLEscape(tmp.Secret))
    http.Redirect(w, r, url, 302)
})
この時、注意しなければならないのがurlfetchにリクエストから作成したコンテキストが必要である事。urlfechを使いたい関数までhttp.Requestを引きずり回さないといけないという、なんとも煩わしい仕様ですがキマリはキマリ。
また認証画面から戻って来た時に再び照合が行える様に、テンポラリトークンをクッキーに保持しておきます。

認証画面から戻ってくると登録していたコールバックURLが呼び出されます。 client := urlfetch.Client(appengine.NewContext(r))
v := r.FormValue("oauth_verifier")
a := strings.Split(r.Header.Get("Cookie"), "=", -1)
w.Header().Set("Set-Cookie""tmp=")
if len(a) != 2 || a[0] != "tmp" {
    http.Error(w, "invalid request"500)
    return
}
a = strings.Split(a[1], "/", -1)
if len(a) != 2 {
    http.Error(w, "invalid request"500)
    return
}
ak, _ := http.URLUnescape(a[0])
as, _ := http.URLUnescape(a[1])
tmp := &Token{ak, as}
token, err := creds.verify(client, tmp, v)
if err != nil {
    http.Error(w, err.String(), 500)
    return
}
クッキーを読み取り得たテンポラリトークンと、認証画面から戻されたベリファイアを合わせて照合を行います。Twitter APIを使ったアプリをバシバシ書いてる諸君なら、鼻くそほじるより簡単ですね。
後は得られたアクセストークンを使ってTwitterにポストする。 gouffu := []string{"ごうっふ~""おうっふ~""もうっふ~""とうっふ~""もふもっふ~""わっふ~""きゃっふ~"}
data := map[string]string{"status": gouffu[rand.Int()%len(gouffu)]}
creds.sign(token, data, resURI, "POST")
resp, err := client.PostForm(resURI, data)
if err != nil {
    http.Error(w, err.String(), 500)
    return
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
    http.Error(w, resp.Status, 500)
    return
}
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
    http.Error(w, err.String(), 500)
    return
}
var m map[string]interface{}
err = json.Unmarshal(b, &m)
if err != nil {
    http.Error(w, err.String(), 500)
    return
}
screen_name := m["user"].(map[string]interface{})["screen_name"].(string)
id_str := m["id_str"].(string)
url := fmt.Sprintf("https://twitter.com/%s/status/%s", screen_name, id_str)
http.Redirect(w, r, url, 302)
単語1つでは遊び心が無いので、何個か用意した。ポストが完了したらパーマリンクへリダイレクトする様にした。
なお、このサーバではクッキーは一時的に保存する事はしてもデータストア等には何も残していません。

ごうっふ~

...

http://go-ouffu.appspot.com/
良かったら遊んでみて下さい。
ソースはこの辺に置いときます。
mattn/go-ouffu - GitHub

ごうっふ~

https://github.com/mattn/go-ouffu
Posted at by



2011/05/29


Go言語がGoogle App Engineで動くようになった事を先日の記事でご紹介しました。
僕に取っては大きな出来事でした。
さて、Go言語用にSDKが提供されていますが、このSDKに含まれるgo-moustachioはアップロードされた画像にヒゲを付けるWebアプリケーションで、以下のURLでも公開されています。
Welcome to Moustachio - Moustachio
http://moustach-io.appspot.com/
仕組みとしては、アップロードされた画像に対してJSON APIから受け取ったパラメータを元に、ヒゲの位置、ヒゲの上向き下向き度合いを変更出来ます。HTML上で合成するのではなく、freetypeを使った画像合成が行われています。
出来上がった画像はGoogle Buzzやtwitterで公開出来るリンクが用意されています。

とても面白いサンプルなのですが、一つ大きな問題が起こりました。
そう、ヒゲが回転出来ないんです。
moustachio1
どう考えても、時計周りに回転が必要ですよね。これでは全世界のヒゲマニアが安眠出来ません。そこでパッチを書きました。
a820df2a6dda - mattnjp-appengine-go - fork of appengine-go - Google Project Hosting

Log message rotate moustachio.

https://code.google.com/r/mattnjp-appengine-go/source/detail?r=a820df2a6ddab19d7c22fe66f8fc76fc41690018&name=default
仕組みとしては、これまで画像イメージに対してfreetypeでヒゲを描いていたのに対して、同じ大きさのRGBA領域に対してヒゲを描画し、ヒゲ画像を回転して元の画像に移しています。
メインとなる部分のコードは以下。
// transcribe points to image from rotated moustache
ra := math.Pi * 2 * float64(angle) / 360
for i := 0; i < h; i++ {
    for j := 0; j < w; j++ {
        xx := int(float64(j-x)*math.Cos(-ra)-float64(i-y)*math.Sin(-ra)) + x
        yy := int(float64(j-x)*math.Sin(-ra)+float64(i-y)*math.Cos(-ra)) + y
        c := mp.At(xx, yy)
        cr, cg, cb, ca := c.RGBA()
        if cr != 0 || cg != 0 || cb != 0 || ca != 0 {
            mrgba.Set(j, i, c)
        }
    }
}
一般的に画像の回転は各ポイントを回転させるのではなく、回転後のポイントに対して元のポイントを求める事で穴の開かない画像が得られます。 本来ならば、オンラインで公開したい所ですが、先日の「appengineにgoをデプロイ出来る権利50名様プレゼント」に外れてしまいましたので、試したい方はローカルサーバでお楽しみ下さい。
moustachio2
ちゃんと綺麗にヒゲが生えました。
Posted at by