google app engineでできる、あんなこと/こんなこと

67
Google App Engineできる、あんなこと/こんなこと (ときどきSlim3) a-know(@a_know)

Upload: a-know

Post on 24-May-2015

6.529 views

Category:

Technology


1 download

DESCRIPTION

Google App Engine/Java(+ slim3)を使って、Webアプリケーションによくあるいろんな機能を実現するために色々と試行錯誤したことについてのお話。「GAEだと、この機能はこうやればできるんだ!」ということを少しでも知ってもらって、一人でも多くの方がGAEで何かを作ってもらえるようになればな~、と思っています!

TRANSCRIPT

Page 1: Google App Engineでできる、あんなこと/こんなこと

Google App Engineで

できる、あんなこと/こんなこと

(ときどきSlim3)

a-know(@a_know)

Page 2: Google App Engineでできる、あんなこと/こんなこと

1.このおはなしの主旨(1)

・前回の勉強会にて、@sinmetalさんが講師をして下さった、 「Google App Engine/Java ハンズオン」。 GAE+slim3を使ったWebアプリケーション開発の初歩を

 学ぶことができました。 ・今日は、その「Google App Engine(with Slim3)」の 一歩(半歩?)踏み込んだ活用例を、ご紹介。 ・特に、ぼくが今までに作ってきたものにおいて活用している ちょっとしたテクニックなどを、宣伝実例を交えながら ご紹介できたら。。

Page 3: Google App Engineでできる、あんなこと/こんなこと

1.このおはなしの主旨(2)

・基本的に、「つくりたいアプリ・サービスを実現するために、a-

knowが調べた結果のご報告」になりますので、間違っているこ

と、すべきでないこともあるかもしれません・・・。そういった場合、ぜひご指摘をお願いします(._.)! ・資料は後ほど後悔公開します!

Page 4: Google App Engineでできる、あんなこと/こんなこと

アジェンダ

・このおはなしの主旨・自己紹介・GAEでtwitter bot!・GAEで利用者ごとのtwitter連携!・GAEで画像のアップロード!・blobstoreを活用する!・そのほか、GAEでできること・課金体系について・おわりに

Page 5: Google App Engineでできる、あんなこと/こんなこと

2.ここで、自己紹介です

                 ・a-know                 ・“えい・のう”と読む                 ・去年まで製造系企業のSIer                 ・今年からはNo Jobs.                  Web系に行きたい!(求職活動中)

・Java/Google App Engine/Android(勉強中。楽しい!)

・bootstrapをよく使ってます・とりあえず「モノ」を作っちゃうのが好き。 悪くいえば、「動けばいい」になっちゃうことも・・・。 ・趣味はバドミントン、雑貨屋巡り。岡山・倉敷・福山・姫路の 雑貨屋のことならなんでも聴いて下さい(・∀・)

Page 6: Google App Engineでできる、あんなこと/こんなこと

言い訳前置きも済んだところで・・・

本題に入ります

Page 7: Google App Engineでできる、あんなこと/こんなこと

この先、自作アプリの宣伝がましくなりますので

ご注意ください...

Page 8: Google App Engineでできる、あんなこと/こんなこと

3.GAEでのTwitter botの作成(1)

・bot?・・・Twitterに自動的につぶやく仕組み・GAEなら、簡単!です!・と、いいますのも、わたくし、週間アスキー連載のマンガ 「カオスだもんね!」名言botをGAEで 作成してます!・まぁ、レポート漫画ですね(迷言・珍言 多数!)。

Page 9: Google App Engineでできる、あんなこと/こんなこと

3.GAEでのTwitter botの作成(1)

・bot?・・・Twitterに自動的につぶやく仕組み・GAEなら、簡単!です!・と、いいますのも、わたくし、週間アスキー連載のマンガ 「カオスだもんね!」名言botをGAEで 作成してます!・まぁ、レポート漫画ですね(迷言・珍言 多数!)。

Page 10: Google App Engineでできる、あんなこと/こんなこと

3.GAEでのTwitter botの作成(2)

・そして嬉しいことに、このbotを「カオスだもんね!」連載の 中で取り上げていただきました!

・...ただ、この掲載によるフォロワーは30程度しか伸びず。「雑誌」というメディアの限界の一端を目の当たりに。。

Page 11: Google App Engineでできる、あんなこと/こんなこと

と、そういう話ではなくて・・・

Page 12: Google App Engineでできる、あんなこと/こんなこと

4.GAEでTwitter botを作るまでの手順

i.twitter developers(https://dev.twitter.com/)で

  アプリケーション登録 ii.Twitter4jをダウンロード、ビルドパスに通す

iii.実装

iv.デプロイ

Page 13: Google App Engineでできる、あんなこと/こんなこと

4.GAEでTwitter botを作るまでの手順

 i.twitter developersでアプリケーション登録

・「twitter botのtwitterアカウント」を作る

 (さっきの場合は@chaos_meigen)・そのアカウントでtwitter developersにサインイン。

Page 14: Google App Engineでできる、あんなこと/こんなこと

4.GAEでTwitter botを作るまでの手順

 i.twitter developersでアプリケーション登録

・「twitter botのtwitterアカウント」を作る (さっきの場合は@chaos_meigen)・そのアカウントでtwitter developersにサインイン。

・「create new application」

Page 15: Google App Engineでできる、あんなこと/こんなこと

4.GAEでTwitter botを作るまでの手順

 i.twitter developersでアプリケーション登録

・「twitter botのtwitterアカウント」を作る (さっきの場合は@chaos_meigen)・そのアカウントでtwitter developersにサインイン。

・「create new application」・適当に入力

Page 16: Google App Engineでできる、あんなこと/こんなこと

4.GAEでTwitter botを作るまでの手順

 i.twitter developersでアプリケーション登録

・「twitter botのtwitterアカウント」を作る (さっきの場合は@chaos_meigen)・そのアカウントでtwitter developersにサインイン。

・「create new application」・適当に入力・Access TokenとAccess Token Secretをメモしておく

Page 17: Google App Engineでできる、あんなこと/こんなこと

4.GAEでTwitter botを作るまでの手順

 i.twitter developersでアプリケーション登録

・「twitter botのtwitterアカウント」を作る (さっきの場合は@chaos_meigen)・そのアカウントでtwitter developersにサインイン。

・「create new application」・適当に入力・Access TokenとAccess Token Secretをメモしておく

Access Token / Access Token Secret:アプリケーションが“そのユーザーとして”ユーザーのアカウントにアクセスするために必要なIDとPasswordみたいなもの

Page 18: Google App Engineでできる、あんなこと/こんなこと

4.GAEでTwitter botを作るまでの手順

 i.twitter developersでアプリケーション登録

・「twitter botのtwitterアカウント」を作る (さっきの場合は@chaos_meigen)・そのアカウントでtwitter developersにサインイン。

・「create new application」・適当に入力・Access TokenとAccess Token Secretをメモしておく・登録後、Access Levelを「Read and write」に変更するのを忘れない!

Page 19: Google App Engineでできる、あんなこと/こんなこと

4.GAEでTwitter botを作るまでの手順

 ii.Twitter4jをダウンロード、ビルドパスに通す

・Twitter4j・・・Twitter APIのJavaラッパ。 ・ダウンロード(http://twitter4j.org/ja/index.html)

・jarを /war/WEB-INF/lib 内にぶっこむ ・ビルドパス通す

Page 20: Google App Engineでできる、あんなこと/こんなこと

4.GAEでTwitter botを作るまでの手順

 iii.実装

・作らなければならないのは・・・ ◎ 「つぶやく内容」を受け取って、ただそれを呟く処理◎ 一定時間ごとに呼び出され、ツイート内容をDatastoreから取得し上述のつぶやく処理を呼び出す処理◎ 上記処理を一定時間ごとにリクエストするcronの設定◎ consumer key、consumer secretの設定◎ cronで呼び出すパスを管理者権限に設定 ・多そうに見えますが、書くコードの量は少ないです

Page 21: Google App Engineでできる、あんなこと/こんなこと

4.GAEでTwitter botを作るまでの手順

 iii.実装

◎「つぶやく内容」を受け取って、ただそれを呟く処理 import twitter4j.Twitter;import twitter4j.TwitterException;import twitter4j.TwitterFactory;import twitter4j.auth.AccessToken; public class TwitterUtil { private String token = "メモったAccess Token";

private String tokenSecret = "メモったAccess Token Secret";

public void doTweet(String tweetContents){ Twitter twitter = new TwitterFactory().getInstance(new AccessToken(token, tokenSecret)); try { twitter.updateStatus(tweetContents); } catch (TwitterException e) { e.printStackTrace(); } }}

Page 22: Google App Engineでできる、あんなこと/こんなこと

4.GAEでTwitter botを作るまでの手順

 iii.実装

◎一定時間ごとに呼び出され、ツイート内容をDatastoreから 取得し上述のつぶやく処理を呼び出す処理  本来ならserviceクラスで行うのが良いです(._.)!

public class MeigenTweetController extends Controller {

@Override public Navigation run() throws Exception { ChaosMeigenMeta meta = new ChaosMeigen(); //tweet内容をランダムで取得

List<Key> keyList = Datastore.query(meta).asKeyList(); int random = (int) Math.floor(Math.random() * keyList.size());//乱数生成

ChaosMeigen cm = Datastore.get(ChaosMeigen.class, keyList.get(random));

//ツイートする

TwitterUtil util = new TwitterUtil(); util.doTweet(cm.getTweetContent); return; }}

Page 23: Google App Engineでできる、あんなこと/こんなこと

4.GAEでTwitter botを作るまでの手順

 iii.実装

◎上記処理を一定時間ごとにリクエストするcronの設定/war/WEB-INF/cron.xml ◎ consumer key、consumer secretの設定/war/WEB-INF/appengine-web.xml

<?xml version="1.0" encoding="UTF-8"?><cronentries> <cron> <url>/meigenTweet</url> <description>Meigen Tweet</description> <schedule>every 30 minutes</schedule> </cron></cronentries>

<system-properties> <property name="oauth.consumerKey" value="xxxxxxxxxxxxxxxxxxxxx" /> <property name="oauth.consumerSecret" value="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" /></system-properties>

Page 24: Google App Engineでできる、あんなこと/こんなこと

4.GAEでTwitter botを作るまでの手順

 iii.実装

◎上記処理を一定時間ごとにリクエストするcronの設定/war/WEB-INF/cron.xml ◎ consumer key、consumer secretの設定/war/WEB-INF/appengine-web.xml

<?xml version="1.0" encoding="UTF-8"?><cronentries> <cron> <url>/meigenTweet</url> <description>Meigen Tweet</description> <schedule>every 30 minutes</schedule> </cron></cronentries>

<system-properties> <property name="oauth.consumerKey" value="xxxxxxxxxxxxxxxxxxxxx" /> <property name="oauth.consumerSecret" value="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" /></system-properties>

Consumer key / Consumer Secret:Twitterにアクセスするアプリケーションを認識するためのもの。

Page 25: Google App Engineでできる、あんなこと/こんなこと

4.GAEでTwitter botを作るまでの手順

 iii.実装

◎ cronで呼び出すパスを管理者権限に設定/war/WEB-INF/web.xml

<security-constraint> <web-resource-collection> <web-resource-name>admin</web-resource-name> <url-pattern>/meigenTweet</url-pattern> </web-resource-collection> <auth-constraint> <role-name>admin</role-name> </auth-constraint></security-constraint>

Page 26: Google App Engineでできる、あんなこと/こんなこと

4.GAEでTwitter botを作るまでの手順

 iii.実装

・作らなければならないのは・・・ ◎ 「つぶやく内容」を受け取って、ただそれを呟く処理◎ 一定時間ごとに呼び出され、ツイート内容をDatastoreから取得し上述のつぶやく処理を呼び出す処理◎ 上記処理を一定時間ごとにリクエストするcronの設定◎ consumer key、consumer secretの設定◎ cronで呼び出すパスを管理者権限に設定◎ もちろん、Modelクラスや名言登録機能も必要(割愛(._.)

Page 27: Google App Engineでできる、あんなこと/こんなこと

一歩進んで・・・

Page 28: Google App Engineでできる、あんなこと/こんなこと

5.利用者ごとのtwitter連携がしたい場合(非bot)

・利用イメージ:拙作「愛用品紹介系サービス『Masterpiece』」

Masterpiece : http://master-piece.appspot.com/

Page 29: Google App Engineでできる、あんなこと/こんなこと

5.利用者ごとのtwitter連携がしたい場合(非bot)

・利用イメージ:拙作「愛用品紹介系サービス『Masterpiece』」・あらかじめtwitter連携をしておけば、 愛用品登録時、合わせてツイートを行うことも可能! ・こういうこと、GAEでも(っていう言い方もおかしいですが)

できます!

Page 30: Google App Engineでできる、あんなこと/こんなこと

6.利用者ごとのtwitter連携の考え方

・基本の考え方は先程のbotといっしょ! ・botのときは「つぶやくユーザーが固定」だったのが、ユーザーごとの連携は「つぶやくユーザーが毎回異なる」ところが違う! ・つまり・・・ユーザーごとのAccess Token、Access Token Secretを保持しておけばいい

Page 31: Google App Engineでできる、あんなこと/こんなこと

7.利用者ごとのtwitter連携を

  実現するための手順(1)

i.認証controllerをつくる

ii.callbackのcontrollerをつくる

iii.連携解除のcontroller/serviceをつくる

Page 32: Google App Engineでできる、あんなこと/こんなこと

7.利用者ごとのtwitter連携を

  実現するための手順(1)

<<認証処理の簡略なイメージ>>

GAE

認証controller

Twitter

callbackcontroller

callback URL

認証完了画面などへ

設定画面

set to session / redirect

save access token/secret

authentication

access token/secret

Page 33: Google App Engineでできる、あんなこと/こんなこと

7.利用者ごとのtwitter連携を

  実現するための手順(2)

i.認証controllerをつくる

・認証を行うためにAuthenticationURLにリダイレクト・その後callbackされるので、callbackの後に必要な情報(ログインIDとか)はsessionに格納しておく。

public class TwitterOauthController extends Controller { @Override public Navigation run() throws Exception { Twitter twitter = new TwitterFactory().getInstance(); RequestToken reqToken = twitter.getOAuthRequestToken("http://master-piece.appspot.com/callback"); sessionScope("twitter", twitter); sessionScope("reqToken", reqToken); sessionScope("loginID", requestScope("loginID")); this.response.sendRedirect(reqToken.getAuthenticationURL()); return null; }}

Page 34: Google App Engineでできる、あんなこと/こんなこと

7.利用者ごとのtwitter連携を

  実現するための手順(3)

ii.callbackのcontrollerをつくる

public class CallbackController extends Controller { @Override public Navigation run() throws Exception { //get from session Twitter twitter = (Twitter) sessionScope("twitter"); RequestToken requestToken = (RequestToken) sessionScope("reqToken"); String loginID = (String) sessionScope("loginID"); String verifier = requestScope("oauth_verifier");//verifier:認証情報

//get AccessToken AccessToken accessToken = null; try { accessToken = twitter.getOAuthAccessToken(requestToken, verifier); this.request.getSession().removeAttribute("reqToken"); } catch (TwitterException e) { e.printStackTrace(); } ・・・

Page 35: Google App Engineでできる、あんなこと/こんなこと

7.利用者ごとのtwitter連携を

  実現するための手順(3)

ii.callbackのcontrollerをつくる

・取得したAccessToken/Secretを、(ここではUserに持たせて)Datastoreに格納

・ツイートが必要なとき(このアプリでいうと、新規アイテム登録時)に改めてAccessToken/Secretを取得、「Twitter twitter = new TwitterFactory().getInstance(new AccessToken(token, tokenSecret);」してやれば、そのユーザーのアカウントでつぶやける!

//save to datastore if(accessToken != null){ Transaction tx = Datastore.beginTransaction(); UserMeta meta = new UserMeta(); User user = Datastore.query(meta).filter(meta.loginID.equal(loginID)).asSingle(); user.setTwitterAccessToken(accessToken.getToken()); user.setTwitterAccessTokenSecret(accessToken.getTokenSecret()); Datastore.put(user); tx.commit(); } this.response.sendRedirect("http://master-piece.appspot.com/user/" + loginID); return null; }}

Page 36: Google App Engineでできる、あんなこと/こんなこと

7.利用者ごとのtwitter連携を

  実現するための手順(4)

iii.連携解除のcontroller/serviceをつくる

public class TwitterReleaseController extends Controller { @Override public Navigation run() throws Exception { String loginID = this.request.getParameter("loginID"); TwitterReleaseService service = new TwitterReleaseService(); service.releaseTwitterTokens(loginID); this.response.sendRedirect("/user/" + loginID); return null; }}

Page 37: Google App Engineでできる、あんなこと/こんなこと

7.利用者ごとのtwitter連携を

  実現するための手順(4)

iii.連携解除のcontroller/serviceをつくる

・やっていることは、ユーザーごとに保持していたAccessToken/Secretを破棄しているだけ。。

・twitter側で保持している「連携アプリの情報」からは、これでは除去できていない・・・それもできて初めて「連携解除」になると思うのだが・・・

・いろいろと探してみたが、連携解除についての記述が見つからず。

わかる方・わかった方はぜひ教えて下さい。

public class TwitterReleaseService { @SuppressWarnings("static-method") public void releaseTwitterTokens(String loginID){ UserMeta meta = new UserMeta(); User user = Datastore.query(meta).filter(meta.loginID.equal(loginID)).asSingle(); user.setTwitterAccessToken(""); user.setTwitterAccessTokenSecret(""); Transaction tx = Datastore.beginTransaction(); Datastore.put(user); tx.commit(); }}

Page 38: Google App Engineでできる、あんなこと/こんなこと

7.利用者ごとのtwitter連携を

  実現するための手順(4)

iii.連携解除のcontroller/serviceをつくる

・やっていることは、ユーザーごとに保持していたAccessToken/Secretを破棄しているだけ。。

・twitter側で保持している「連携アプリの情報」からは、これでは除去できていない・・・それもできて初めて「連携解除」になると思うのだが・・・

・いろいろと探してみたが、連携解除についての記述が見つからず。

わかる方・わかった方はぜひ教えて下さい。

public class TwitterReleaseService { @SuppressWarnings("static-method") public void releaseTwitterTokens(String loginID){ UserMeta meta = new UserMeta(); User user = Datastore.query(meta).filter(meta.loginID.equal(loginID)).asSingle(); user.setTwitterAccessToken(""); user.setTwitterAccessTokenSecret(""); Transaction tx = Datastore.beginTransaction(); Datastore.put(user); tx.commit(); }}

Page 39: Google App Engineでできる、あんなこと/こんなこと

8.画像のアップロードは?

・GAE上のWebアプリケーションへの画像のアップロードは・・・

 もちろんできます!

・拙作「Masterpiece」でも、このとおり。 ・ただ、GAEの制約上、アプリケーション内で「ファイル」を扱う・出力することはできないので・・・→ 「blobstore」を使います!

Page 40: Google App Engineでできる、あんなこと/こんなこと

9.「blobstore」って?

・blob=Binary Large OBject ・要は「でっかいバイナリデータの記録専用のdatastore」と思えばおk

・アップロードするとBlobKeyが得られる。BlobKeyをキーにblobを取得することが可能

・アップロードしたblobを変更することはできない。削除は可能

The Blobstore API allows your application to serve data objects, called blobs, that are much larger than the size allowed for objects in the Datastore service. Blobs are useful for serving large files, such as video or image files, and for allowing users to upload large data files. Blobs are created by uploading a file through an HTTP request. Typically, your applications will do this by presenting a form with a file upload field to the user. When the form is submitted, the Blobstore creates a blob from the file's contents and returns an opaque reference to the blob, called a blob key, which you can later use to serve the blob. The application can serve the complete blob value in response to a user request, or it can read the value directly using a streaming file-like interface. Google Developers (https://developers.google.com/appengine/docs/java/blobstore/overview?hl=en)

Page 41: Google App Engineでできる、あんなこと/こんなこと

10.画像アップロードの一番簡単な実装例(1)

・画面表示時に、actionタグ内URL(blobstoreへの画像アップロード用の)が

生成される

// file index.jsp <%@ page import="com.google.appengine.api.blobstore.BlobstoreServiceFactory" %><%@ page import="com.google.appengine.api.blobstore.BlobstoreService" %><% BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();%><html> <head><title>Upload Test</title></head> <body> <form action="<%= blobstoreService.createUploadUrl("/upload") %>" method="post" 

enctype="multipart/form-data"> <input type="text" name="foo"> <input type="file" name="myFile"> <input type="submit" value="Submit"> </form> </body></html>

◎ フロントエンド

Page 42: Google App Engineでできる、あんなこと/こんなこと

10.画像アップロードの一番簡単な実装例(2)

// file Upload.java public class Upload extends HttpServlet { private BlobstoreService bs = BlobstoreServiceFactory.getBlobstoreService();

public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { Map<String, BlobKey> blobs = bs.getUploadedBlobs(req); BlobKey blobKey = blobs.get("myFile"); if (blobKey == null) { res.sendRedirect("/"); } else { res.sendRedirect("/serve?blob-key=" + blobKey.getKeyString()); } }}

◎ callback処理

・callbackとしての処理。

ここに来ている時点で、アップロードは既に完了している(BlobKeyを取り出すだけ)

Page 43: Google App Engineでできる、あんなこと/こんなこと

10.画像アップロードの一番簡単な実装例(3)

// file Serve.java public class Serve extends HttpServlet { private BlobstoreService bs = BlobstoreServiceFactory.getBlobstoreService(); public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException { BlobKey blobKey = new BlobKey(req.getParameter("blob-key")); bs.serve(blobKey, res); }}

◎ アップロードした画像の取得

・元のサンプルはここ(https://developers.google.com/appengine/docs/java/blobstore/overview?hl=ja#Complete_Sample_App)

Page 44: Google App Engineでできる、あんなこと/こんなこと

11.画像アップロード・tips(1)

ImagesService imagesService = ImagesServiceFactory.getImagesService();String url = imagesService.getServingUrl(e.getBlobKey());

・画像データの取得方法は、responseに渡す方法しかないのか?

→ImagesServiceを利用すると、Blobstoreに保存した画像にアクセスできるURLを

取得することができます。(SinDiary : http://d.hatena.ne.jp/sinmetal/20120206/1328544215 )

以下のようにすると、画像の長辺サイズの指定も可能。 ・その他、サイズを指定してのリサイズ、回転・反転などもできる。詳しくは公式

(https://developers.google.com/appengine/docs/java/images/overview?hl=en )。

String url = imagesService.getServingUrl(e.getBlobKey(), 512, false);

Page 45: Google App Engineでできる、あんなこと/こんなこと

11.画像アップロード・tips(1)

ImagesService imagesService = ImagesServiceFactory.getImagesService();String url = imagesService.getServingUrl(e.getBlobKey());

・画像データの取得方法は、responseに渡す方法しかないのか?

→ImagesServiceを利用すると、Blobstoreに保存した画像にアクセスできるURLを

取得することができます。(SinDiary : http://d.hatena.ne.jp/sinmetal/20120206/1328544215 )

以下のようにすると、画像の長辺サイズの指定も可能。 ・その他、サイズを指定してのリサイズ、回転・反転などもできる。詳しくは公式

(https://developers.google.com/appengine/docs/java/images/overview?hl=en )。

String url = imagesService.getServingUrl(e.getBlobKey(), 512, false);

指定可能なサイズ:32, 48, 64, 72, 80, 90, 94, 104, 110, 120, 128, 144, 150,160, 200, 220, 288, 320, 400, 512, 576, 640, 720, 800, 912,1024, 1152, 1280, 1440, 1600

Page 46: Google App Engineでできる、あんなこと/こんなこと

11.画像アップロード・tips(2)

・画像データと同時に、他のデータも送ることができるか?

→ できますが、文字化け対策が必要。例えば、以下の例だと・・・

Page 47: Google App Engineでできる、あんなこと/こんなこと

11.画像アップロード・tips(2)

・画像データと同時に、他のデータも送ることができるか?

→ できますが、文字化け対策が必要。例えば、以下の例だと・・・

String categoryCode = requestScope("CategoryCode"); String itemTitle = requestScope("ItemTitle"); String itemComment = requestScope("ItemComment"); System.out.println(categoryCode);System.out.println(itemTitle);System.out.println(itemComment);

01??????????????????????????????????

Page 48: Google App Engineでできる、あんなこと/こんなこと

11.画像アップロード・tips(2)

・画像データと同時に、他のデータも送ることができるか?

→ できますが、文字化け対策が必要。例えば、以下の例だと・・・

String categoryCode = requestScope("CategoryCode"); String itemTitle = requestScope("ItemTitle"); String itemComment = requestScope("ItemComment"); System.out.println(categoryCode);System.out.println(itemTitle);System.out.println(itemComment);

01??????????????????????????????????

画像データ以外にもタイトルや説明文なども一緒にform submitしたいわけですが・・・

そのままsubmitすると、マルチバイト文字が文字化けしてしまいます!

Page 49: Google App Engineでできる、あんなこと/こんなこと

11.画像アップロード・tips(2)

・原因は???だが・・・「非マルチバイト文字だけの場合はエンコードせず、マルチバイト文字が 含まれていた場合だけbase64でエンコードされる」というGAEの仕様が問題? ・とりあえず、「POSTする前に全部 base64でエンコードしてから送信するという方法」

で回避できている方がおられる様子(※ GAE/Python)。 (http://groups.google.com/group/google-app-engine-

japan/browse_thread/thread/6df5a80f70d0d1fa  か?ただこれも、GAE/Python...

これ以外には、これが原因じゃないかと思えるものが見つかりませんでした)

Page 50: Google App Engineでできる、あんなこと/こんなこと

11.画像アップロード・tips(3)

・で、こう対処しました。 i.JavaScriptを使って、送信前に全てのデータ(マルチバイト文字を含む可能性がある

項目)をbase64エンコードする。(JavaScriptでのエンコードは、こちらhttp://user1.matsumoto.ne.jp/~goma/js/base64.htmlとか。

 まぁggってみてください ) ii.サーバサイドではこうする。

もっとスマートな方法があるのかもしれません。教えてもらえると幸いです。

title_hidden.value = base64.encode( title.value, 1 );

byte[] outStrBtye_title = Base64.decodeBase64(requestScope("ItemTitle").getBytes());String name = new String(outStrBtye_title, "utf-8");

Page 51: Google App Engineでできる、あんなこと/こんなこと

11.画像アップロード・tips(4)

・他に注意点は?→画面表示時に生成されたURLには有効期間があります。

特に、「アップロードする画像を選んで、それに関する説明を入力する」というようなサービスだと、この有効期間(体感で10分~15分)を簡単に過ぎてしまって、「Bad Request - The upload URL has expired」が出てしまいます。

<form action="<%= blobstoreService.createUploadUrl("/upload") %>" method="post" enctype="multipart/form-data">

...(´・ω・`)

Page 52: Google App Engineでできる、あんなこと/こんなこと

11.画像アップロード・tips(4)

・対策としては、「submitしたときに」アップロード用のURLを生成し、formのactionを書き換えること。(ここではjQueryの$.get使用)

<form action="" method="post" enctype="multipart/form-data" name="uploadForm"> <input type="text" name="foo"> <input type="file" name="myFile"> <input type="button" value="Submit" onclick="submitButtonClick();"></form>

function submitButtonClick(){ $.get("/getBlobUrl", function(data){ document.uploadForm.action = data.url; document.uploadForm.submit(); }, 'json'); }

Page 53: Google App Engineでできる、あんなこと/こんなこと

11.画像アップロード・tips(4)

・対策としては、「submitしたときに」アップロード用のURLを生成し、formのactionを書き換えること。(ここではjQueryの$.get使用)

public class GetBlobUrlController extends Controller { @override public Navigation run() throws Exception { res.setContentType("application/json; charset=UTF-8"); BlobstoreService bs = BlobstoreServiceFactory.getBlobstoreService(); String url = bs.createUploadUrl("/upload"); Map<String, String> map = new HashMap<String, String>(); map.put("url", url); JSON.encode(map, this.response.getOutputStream()); } }}

Page 54: Google App Engineでできる、あんなこと/こんなこと

12.めんどくさっ!!

・ですよね(´・ω・`)。。 ・「最低限のことをさせる(サンプルアプリレベル)までは簡単」 だけど、 「普通にアプリケーションとして使えるレベルにする」となると、

結構めんどくさいことが多々あるんだよなーと、

ぼくも思いました・・・

Page 55: Google App Engineでできる、あんなこと/こんなこと

13.blobstoreの活用方法って、それぐらい?

・静的ファイルとして、ではないですが、擬似的にファイルの書き出しが行えるので、 それをうまく活用することはできるかと思います ・例えば・・・。 datastoreだと、登録する際には、1エンティティ1MBまでという制約があります

・たいていのWebアプリケーションであれば、この制約を気にする機会は あまりないと思うのですが、拙作「sa-boom(サブーン)!!」というWebアプリでは・・・

sa-boom!! : http://sa-boom.appspot.com/

Page 56: Google App Engineでできる、あんなこと/こんなこと

14.iTunes再生回数解析&共有サービス

「sa-boom!!」の仕様・仕組み

・利用者が自由に指定できる「起点」「終点」のそれぞれの期間で、 iTunes再生回数の差分を取れるアプリケーション。

たとえば「2012年3月1日から2012年3月31日までの再生回数」で

ランキング表示したりできる。

→ iTunes再生回数の情報は、別途「sa-boom!! client」で

  アップロードしておくものとする ・内部的には、大雑把にいうと、「曲名-再生回数」の巨大なMapを持っているイメージ ・ユーザーがどの期間ごとに差分指定をするかはわからない→ 例え再生回数が1回しかない曲のものでも、

  全曲分の情報を保持しておかなくてはいけない(あらかじめ差分期間を想定できたり、そもそも差分を行わないのであれば 上位○曲分の情報だけ持っていればいいけど)

Page 57: Google App Engineでできる、あんなこと/こんなこと

15.blobstore利用までの苦労の変遷

・この「sa-boom!!」、実は今年2月にリニューアルしたもの。 これにより、この巨大なMapデータの持ち方も最適化できた

・それまでは...  i.普通にdatastoreに登録するModelの一属性として持たせる

  → ちょっと音楽が好きな人なら、すぐに1MBこえる ii.Mapをserializeしてバイト配列にしたものをzip圧縮、それを持たせる  → ちょっと音楽が好(ry iii.zip圧縮したバイト配列を、 datastoreへの登録が問題ないレベルまで分割。親子関係を持たせて登録

  → やっと登録はできるようになった。が、コードがスパゲッティに ・今はもうこんな苦労をしなくていい!

Page 58: Google App Engineでできる、あんなこと/こんなこと

16.blobstoreにblobを登録する(1)

・使う要素としては、 「Writing Files to the Blobstore (Experimental)」を活用する。

(https://developers.google.com/appengine/docs/java/blobstore/overview?

hl=en#Writing_Files_to_the_Blobstore ) ・書いてあるとおり、「Experimental」なので、 実装方法が変わる・いきなり使えなくなるなどのリスクはあるのでご注意

Page 59: Google App Engineでできる、あんなこと/こんなこと

16.blobstoreにblobを登録する(2)

・まずblobstoreへの登録部分。今までの名残でzip圧縮もしてます public static BlobKey registBlob(Object o) throws IOException{ //データをバイト配列に変換(圧縮も実施)

ByteArrayOutputStream baos = new ByteArrayOutputStream(); ZipOutputStream zip_os = new ZipOutputStream(baos); zip_os.putNextEntry(new ZipEntry("zipped_entry")); ObjectOutputStream oos = new ObjectOutputStream(zip_os); oos.reset(); oos.writeObject(o); oos.flush(); zip_os.closeEntry(); zip_os.close(); baos.close(); byte[] bytes = baos.toByteArray();        ・・・

Page 60: Google App Engineでできる、あんなこと/こんなこと

16.blobstoreにblobを登録する(3)

・mime-typeを"application/octet-stream"にしてやることにより、 渡されたオブジェクトをバイト配列にしたもの(+zip圧縮)を、 blobstoreにバイナリデータファイルとして書き出せています。と思います。・書き込みが成功するとBlobKeyが得られるのは、画像のアップロードのときと同じ

       ・・・ FileService fileService = FileServiceFactory.getFileService(); // Create a new Blob file with mime-type "application/octet-stream" AppEngineFile file = fileService.createNewBlobFile("application/octet-stream"); // Open a channel to write to it boolean lock = true; FileWriteChannel writeChannel = fileService.openWriteChannel(file, lock); // This time we write to the channel using standard Java writeChannel.write(ByteBuffer.wrap(bytes)); // Now finalize writeChannel.closeFinally(); return fileService.getBlobKey(file);}

Page 61: Google App Engineでできる、あんなこと/こんなこと

16.blobstoreにblobを登録する(4)

・続いて、blobstoreに書きだしたバイナリデータ(ファイル)を読み込む方法です ・これで、一手間はかかるけれど、巨大なデータを扱うWebアプリケーションの開発も、gaeで可能!

InputStream is = new BlobstoreInputStream(blobkey); ZipInputStream zip_in = new ZipInputStream(is);zip_in.getNextEntry();ObjectInputStream ois = new ObjectInputStream(zip_in); HashMap<String, Integer> readObject = (HashMap<String, Integer>) ois.readObject();

Page 62: Google App Engineでできる、あんなこと/こんなこと

17.他にどんなことができるの?

・GAEアプリケーションは、メールの送信だけでなく、 メールの受信・それをトリガにした処理の実行も可能です!

→GAEはメールを受信すると、

「/_ah/mail/メールアドレス」というURL呼び出しに変換する

・画像ファイルやhtmlファイルなどの静的ファイルを配置することも可能。→Javaロジックの絡まない、ちょっとしたホームページ作りにもどうぞ!

・その他、GAEでのアプリ作成を楽しんでみたい!という方へ...

えいのうのいえ: http://a-know-home.appspot.com/

Page 63: Google App Engineでできる、あんなこと/こんなこと

17.他にどんなことができるの?

・GAEアプリケーションは、メールの送信だけでなく、 メールの受信・それをトリガにした処理の実行も可能です!

→GAEはメールを受信すると、

「/_ah/mail/メールアドレス」というURL呼び出しに変換する

・画像ファイルやhtmlファイルなどの静的ファイルを配置することも可能。→Javaロジックの絡まない、ちょっとしたホームページ作りにもどうぞ!

・その他、GAEでのアプリ作成を楽しんでみたい!という方へ...

作ればわかる!Google App Engineプログラミング(翔泳社・中垣健志 著)

えいのうのいえ: http://a-know-home.appspot.com/

Page 64: Google App Engineでできる、あんなこと/こんなこと

18.課金体系について(1)

・GAEって、こないだのpreview卒業で課金体系が値上げ方向に変わったんでしょう? ・GAEって、単純に高いんじゃないの? →そもそも、無料のままでも結構なレベルまで行えますが・・・。

GAEに合ったチューニング・設定をしなければ、そりゃ高くなります!

財布に穴が空いているも同じ!(とはいうぼくも、まだまだザルです)

Page 65: Google App Engineでできる、あんなこと/こんなこと

18.課金体系について(2)

・一口に「チューニング」「設定」といっても、高度なものから非常に簡単なものからまで。 まずは「appengine ja night #18で分かった、Google App Engineの

課金の仕組み、節約術・自分用まとめ」(http://d.hatena.ne.jp/a-know/20111126)を見て、

「今すぐにでもできるチューニング・設定」を知りましょう!

・最新のガイドも出版されました!ぼくも買いました! 「今のGAE」を、いっしょに勉強しましょう!

・てか、課金に悩まされるくらいのHitアプリを作ってみたい

もんです!(魂の叫び)

Google API Expertが解説する

Google App Engine for Java実践ガイド

(インプレスジャパン・小川 信一(@shin1ogawa) 著)

Page 66: Google App Engineでできる、あんなこと/こんなこと

19.おわりに

・Google App Engine/Java(+ slim3)を使って、 Webアプリケーションによくあるいろんな機能を実現するためのお話でした

・正直・・・めんどくさい部分もあります、ロックインしなきゃいけない部分も あります(datastore周りとか)

・でも、他のPaaS、IaaSやVPSなどを触ったことがあるわけではないのですが、 これだけ純粋に「アプリケーションの開発」に集中・注力できるのは魅力!

(他のサービスにも興味はあるけど)

・「作りたい何か」があって、「ひとまずはそれを世に出したい」、 「それを通じて勉強したい」という人にはうってつけのプラットフォームかな、と!

・もし、今日おはなしした内容を後日、試されたときに わからないことなどがあれば、いつでも!@a_knowまで、お気軽に!

(GAEの場合は、ハッシュタグ#gaeja、#slim3 も、親切な方が多いです!)

Page 67: Google App Engineでできる、あんなこと/こんなこと

ご清聴、ありがとうございました!