• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
Google App Engineでできる、あんなこと/こんなこと
 

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

on

  • 5,736 views

Google App Engine/Java(+ slim3)を使って、Webアプリケーションによくあるいろんな機能を実現するために色々と試行錯誤したことについてのお話。

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

Statistics

Views

Total Views
5,736
Views on SlideShare
5,083
Embed Views
653

Actions

Likes
2
Downloads
10
Comments
0

4 Embeds 653

http://d.hatena.ne.jp 640
http://mj89sp3sau2k7lj1eg3k40hkeppguj6j-a-sites-opensocial.googleusercontent.com 11
http://webcache.googleusercontent.com 1
https://twitter.com 1

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    Google App Engineでできる、あんなこと/こんなこと Google App Engineでできる、あんなこと/こんなこと Presentation Transcript

    • Google App Engineでできる、あんなこと/こんなこと(ときどきSlim3) a-know(@a_know)
    • 1.このおはなしの主旨(1)・前回の勉強会にて、@sinmetalさんが講師をして下さった、「Google App Engine/Java ハンズオン」。 GAE+slim3を使ったWebアプリケーション開発の初歩を 学ぶことができました。・今日は、その「Google App Engine(with Slim3)」の 一歩(半歩?)踏み込んだ活用例を、ご紹介。・特に、ぼくが今までに作ってきたものにおいて活用している ちょっとしたテクニックなどを、宣伝実例を交えながら ご紹介できたら。。
    • 1.このおはなしの主旨(2)・基本的に、「つくりたいアプリ・サービスを実現するために、a-knowが調べた結果のご報告」になりますので、間違っていること、すべきでないこともあるかもしれません・・・。そういった場合、ぜひご指摘をお願いします(._.)!・資料は後ほど後悔公開します!
    • アジェンダ・このおはなしの主旨・自己紹介・GAEでtwitter bot!・GAEで利用者ごとのtwitter連携!・GAEで画像のアップロード!・blobstoreを活用する!・そのほか、GAEでできること・課金体系について・おわりに
    • 2.ここで、自己紹介です                 ・a-know                 ・“えい・のう”と読む                 ・去年まで製造系企業のSIer                 ・今年からはNo Jobs.                  Web系に行きたい!(求職活動中)・Java/Google App Engine/Android(勉強中。楽しい!)・bootstrapをよく使ってます・とりあえず「モノ」を作っちゃうのが好き。 悪くいえば、「動けばいい」になっちゃうことも・・・。・趣味はバドミントン、雑貨屋巡り。岡山・倉敷・福山・姫路の 雑貨屋のことならなんでも聴いて下さい(・∀・)
    • 言い訳前置きも済んだところで・・・ 本題に入ります
    • この先、自作アプリの宣伝がましくなりますので ご注意ください...
    • 3.GAEでのTwitter botの作成(1)・bot?・・・Twitterに自動的につぶやく仕組み・GAEなら、簡単!です!・と、いいますのも、わたくし、週間アスキー連載のマンガ 「カオスだもんね!」名言botをGAEで 作成してます!・まぁ、レポート漫画ですね(迷言・珍言 多数!)。
    • 3.GAEでのTwitter botの作成(1)・bot?・・・Twitterに自動的につぶやく仕組み・GAEなら、簡単!です!・と、いいますのも、わたくし、週間アスキー連載のマンガ 「カオスだもんね!」名言botをGAEで 作成してます!・まぁ、レポート漫画ですね(迷言・珍言 多数!)。
    • 3.GAEでのTwitter botの作成(2)・そして嬉しいことに、このbotを「カオスだもんね!」連載の 中で取り上げていただきました!・...ただ、この掲載によるフォロワーは30程度しか伸びず。「雑誌」というメディアの限界の一端を目の当たりに。。
    • と、そういう話ではなくて・・・
    • 4.GAEでTwitter botを作るまでの手順i.twitter developers(https://dev.twitter.com/)で  アプリケーション登録ii.Twitter4jをダウンロード、ビルドパスに通すiii.実装iv.デプロイ
    • 4.GAEでTwitter botを作るまでの手順 i.twitter developersでアプリケーション登録・「twitter botのtwitterアカウント」を作る (さっきの場合は@chaos_meigen)・そのアカウントでtwitter developersにサインイン。
    • 4.GAEでTwitter botを作るまでの手順 i.twitter developersでアプリケーション登録・「twitter botのtwitterアカウント」を作る (さっきの場合は@chaos_meigen)・そのアカウントでtwitter developersにサインイン。・「create new application」
    • 4.GAEでTwitter botを作るまでの手順 i.twitter developersでアプリケーション登録・「twitter botのtwitterアカウント」を作る (さっきの場合は@chaos_meigen)・そのアカウントでtwitter developersにサインイン。・「create new application」・適当に入力
    • 4.GAEでTwitter botを作るまでの手順 i.twitter developersでアプリケーション登録・「twitter botのtwitterアカウント」を作る (さっきの場合は@chaos_meigen)・そのアカウントでtwitter developersにサインイン。・「create new application」・適当に入力・Access TokenとAccess Token Secretをメモしておく
    • 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みたいなもの
    • 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」に変更するのを忘れない!
    • 4.GAEでTwitter botを作るまでの手順 ii.Twitter4jをダウンロード、ビルドパスに通す・Twitter4j・・・Twitter APIのJavaラッパ。・ダウンロード(http://twitter4j.org/ja/index.html)・jarを /war/WEB-INF/lib 内にぶっこむ・ビルドパス通す
    • 4.GAEでTwitter botを作るまでの手順 iii.実装・作らなければならないのは・・・◎ 「つぶやく内容」を受け取って、ただそれを呟く処理◎ 一定時間ごとに呼び出され、ツイート内容をDatastoreから取得し上述のつぶやく処理を呼び出す処理◎ 上記処理を一定時間ごとにリクエストするcronの設定◎ consumer key、consumer secretの設定◎ cronで呼び出すパスを管理者権限に設定・多そうに見えますが、書くコードの量は少ないです
    • 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(); } }}
    • 4.GAEでTwitter botを作るまでの手順 iii.実装◎一定時間ごとに呼び出され、ツイート内容をDatastoreから 取得し上述のつぶやく処理を呼び出す処理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; }} 本来ならserviceクラスで行うのが良いです(._.)!
    • 4.GAEでTwitter botを作るまでの手順 iii.実装◎上記処理を一定時間ごとにリクエストするcronの設定/war/WEB-INF/cron.xml <?xml version="1.0" encoding="UTF-8"?> <cronentries> <cron> <url>/meigenTweet</url> <description>Meigen Tweet</description> <schedule>every 30 minutes</schedule> </cron> </cronentries>◎ consumer key、consumer secretの設定/war/WEB-INF/appengine-web.xml<system-properties> <property name="oauth.consumerKey" value="xxxxxxxxxxxxxxxxxxxxx" /> <property name="oauth.consumerSecret" value="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" /></system-properties>
    • 4.GAEでTwitter botを作るまでの手順 iii.実装◎上記処理を一定時間ごとにリクエストするcronの設定/war/WEB-INF/cron.xml <?xml version="1.0" encoding="UTF-8"?> <cronentries> <cron> <url>/meigenTweet</url> <description>Meigen Tweet</description> <schedule>every 30 minutes</schedule> </cron> </cronentries>◎ consumer key、consumer secretの設定/war/WEB-INF/appengine-web.xml Consumer key / Consumer Secret:<system-properties> Twitterにアクセスするアプリケーションを認識するためのもの。 <property name="oauth.consumerKey" value="xxxxxxxxxxxxxxxxxxxxx" /> <property name="oauth.consumerSecret" value="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" /></system-properties>
    • 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>
    • 4.GAEでTwitter botを作るまでの手順 iii.実装・作らなければならないのは・・・◎ 「つぶやく内容」を受け取って、ただそれを呟く処理◎ 一定時間ごとに呼び出され、ツイート内容をDatastoreから取得し上述のつぶやく処理を呼び出す処理◎ 上記処理を一定時間ごとにリクエストするcronの設定◎ consumer key、consumer secretの設定◎ cronで呼び出すパスを管理者権限に設定◎ もちろん、Modelクラスや名言登録機能も必要(割愛(._.)
    • 一歩進んで・・・
    • 5.利用者ごとのtwitter連携がしたい場合(非bot) ・利用イメージ:拙作「愛用品紹介系サービス『Masterpiece』」 Masterpiece : http://master-piece.appspot.com/
    • 5.利用者ごとのtwitter連携がしたい場合(非bot) ・利用イメージ:拙作「愛用品紹介系サービス『Masterpiece』」 ・あらかじめtwitter連携をしておけば、  愛用品登録時、合わせてツイートを行うことも可能! ・こういうこと、GAEでも(っていう言い方もおかしいですが) できます!
    • 6.利用者ごとのtwitter連携の考え方・基本の考え方は先程のbotといっしょ!・botのときは「つぶやくユーザーが固定」だったのが、ユーザーごとの連携は「つぶやくユーザーが毎回異なる」ところが違う!・つまり・・・ユーザーごとのAccess Token、Access Token Secretを保持しておけばいい
    • 7.利用者ごとのtwitter連携を  実現するための手順(1)i.認証controllerをつくるii.callbackのcontrollerをつくるiii.連携解除のcontroller/serviceをつくる
    • 7.利用者ごとのtwitter連携を  実現するための手順(1)<<認証処理の簡略なイメージ>> 設定画面 set to session / redirectGAE Twitter save access token/secret 認証controller authentication callback URL access token/secret callback controller 認証完了画面などへ
    • 7.利用者ごとのtwitter連携を   実現するための手順(2) i.認証controllerをつくる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; }} ・認証を行うためにAuthenticationURLにリダイレクト・その後callbackされるので、callback の後に必要な情報(ログインIDとか)はsessionに格納しておく。
    • 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(); } ・・・
    • 7.利用者ごとのtwitter連携を   実現するための手順(3)ii.callbackのcontrollerをつくる //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; }}・取得したAccessToken/Secretを、(ここではUserに持たせて)Datastoreに格納・ツイートが必要なとき(このアプリでいうと、新規アイテム登録時)に改めてAccessToken/Secretを取得、「Twitter twitter = new TwitterFactory().getInstance(new AccessToken(token, tokenSecret);」してやれば、そのユーザーのアカウントでつぶやける!
    • 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; }}
    • 7.利用者ごとのtwitter連携を   実現するための手順(4)iii.連携解除のcontroller/serviceをつくる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(); }}・やっていることは、ユーザーごとに保持していたAccessToken/Secretを破棄しているだけ。。・twitter側で保持している「連携アプリの情報」からは、これでは除去できていない・・・それもできて初めて「連携解除」になると思うのだが・・・・いろいろと探してみたが、連携解除についての記述が見つからず。わかる方・わかった方はぜひ教えて下さい。
    • 7.利用者ごとのtwitter連携を   実現するための手順(4)iii.連携解除のcontroller/serviceをつくる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(); }}・やっていることは、ユーザーごとに保持していたAccessToken/Secretを破棄しているだけ。。・twitter側で保持している「連携アプリの情報」からは、これでは除去できていない・・・それもできて初めて「連携解除」になると思うのだが・・・・いろいろと探してみたが、連携解除についての記述が見つからず。わかる方・わかった方はぜひ教えて下さい。
    • 8.画像のアップロードは?・GAE上のWebアプリケーションへの画像のアップロードは・・・ もちろんできます!・拙作「Masterpiece」でも、このとおり。・ただ、GAEの制約上、アプリケーション内で「ファイル」を扱う・出力することはできないので・・・→ 「blobstore」を使います!
    • 9.「blobstore」って?・blob=Binary Large OBjectThe Blobstore API allows your application to serve data objects, called blobs, that are muchlarger than the size allowed for objects in the Datastore service. Blobs are useful forserving large files, such as video or image files, and for allowing users to upload large datafiles. Blobs are created by uploading a file through an HTTP request. Typically, yourapplications will do this by presenting a form with a file upload field to the user. When theform is submitted, the Blobstore creates a blob from the files contents and returns an opaquereference to the blob, called a blob key, which you can later use to serve the blob. Theapplication can serve the complete blob value in response to a user request, or it can readthe value directly using a streaming file-like interface.Google Developers (https://developers.google.com/appengine/docs/java/blobstore/overview?hl=en)・要は「でっかいバイナリデータの記録専用のdatastore」と思えばおk・アップロードするとBlobKeyが得られる。BlobKeyをキーにblobを取得することが可能・アップロードしたblobを変更することはできない。削除は可能
    • 10.画像アップロードの一番簡単な実装例(1)◎ フロントエンド// 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>・画面表示時に、actionタグ内URL(blobstoreへの画像アップロード用の)が生成される
    • 10.画像アップロードの一番簡単な実装例(2)◎ callback処理// file Upload.javapublic 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としての処理。ここに来ている時点で、アップロードは既に完了している(BlobKeyを取り出すだけ)
    • 10.画像アップロードの一番簡単な実装例(3)◎ アップロードした画像の取得// file Serve.javapublic 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)
    • 11.画像アップロード・tips(1)・画像データの取得方法は、responseに渡す方法しかないのか?→ImagesServiceを利用すると、Blobstoreに保存した画像にアクセスできるURLを取得することができます。(SinDiary : http://d.hatena.ne.jp/sinmetal/20120206/1328544215 )ImagesService imagesService = ImagesServiceFactory.getImagesService();String url = imagesService.getServingUrl(e.getBlobKey());以下のようにすると、画像の長辺サイズの指定も可能。String url = imagesService.getServingUrl(e.getBlobKey(), 512, false);・その他、サイズを指定してのリサイズ、回転・反転などもできる。詳しくは公式(https://developers.google.com/appengine/docs/java/images/overview?hl=en )。
    • 11.画像アップロード・tips(1)・画像データの取得方法は、responseに渡す方法しかないのか?→ImagesServiceを利用すると、Blobstoreに保存した画像にアクセスできるURLを取得することができます。(SinDiary : http://d.hatena.ne.jp/sinmetal/20120206/1328544215 )ImagesService imagesService = ImagesServiceFactory.getImagesService();String url = imagesService.getServingUrl(e.getBlobKey());以下のようにすると、画像の長辺サイズの指定も可能。 指定可能なサイズ: 32, 48, 64, 72, 80, 90, 94, 104, 110, 120, 128, 144, 150,String url = imagesService.getServingUrl(e.getBlobKey(), 512, false); 160, 200, 220, 288, 320, 400, 512, 576, 640, 720, 800, 912, 1024, 1152, 1280, 1440, 1600・その他、サイズを指定してのリサイズ、回転・反転などもできる。詳しくは公式(https://developers.google.com/appengine/docs/java/images/overview?hl=en )。
    • 11.画像アップロード・tips(2)・画像データと同時に、他のデータも送ることができるか?→ できますが、文字化け対策が必要。例えば、以下の例だと・・・
    • 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 ?????? ????????????????????????????
    • 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);画像データ以外にもタイトルや説明文なども一緒にform submitしたいわけですが・・・そのままsubmitすると、マルチバイト文字が文字化けしてしまいます! 01 ?????? ????????????????????????????
    • 11.画像アップロード・tips(2)・原因は???だが・・・「非マルチバイト文字だけの場合はエンコードせず、マルチバイト文字が 含まれていた場合だけbase64でエンコードされる」というGAEの仕様が問題?・とりあえず、「POSTする前に全部 base64でエンコードしてから送信するという方法」で回避できている方がおられる様子(※ GAE/Python)。(http://groups.google.com/group/google-app-engine-japan/browse_thread/thread/6df5a80f70d0d1fa  か?ただこれも、GAE/Python...これ以外には、これが原因じゃないかと思えるものが見つかりませんでした)
    • 11.画像アップロード・tips(3)・で、こう対処しました。i.JavaScriptを使って、送信前に全てのデータ(マルチバイト文字を含む可能性がある項目)をbase64エンコードする。(JavaScript でのエンコードは、こちらhttp://user1.matsumoto.ne.jp/~goma/js/base64.htmlとか。 まぁggってみてください )title_hidden.value = base64.encode( title.value, 1 );ii.サーバサイドではこうする。byte[] outStrBtye_title = Base64.decodeBase64(requestScope("ItemTitle").getBytes());String name = new String(outStrBtye_title, "utf-8");もっとスマートな方法があるのかもしれません。教えてもらえると幸いです。
    • 11.画像アップロード・tips(4)・他に注意点は?→画面表示時に生成されたURLには有効期間があります。<form action="<%= blobstoreService.createUploadUrl("/upload") %>" method="post" enctype="multipart/form-data">特に、「アップロードする画像を選んで、それに関する説明を入力する」というようなサービスだと、この有効期間(体感で10分~15分)を簡単に過ぎてしまって、「Bad Request -The upload URL has expired」が出てしまいます。 ...(´・ω・`)
    • 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); }
    • 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()); } }}
    • 12.めんどくさっ!!・ですよね(´・ω・`)。。・「最低限のことをさせる(サンプルアプリレベル)までは簡単」 だけど、 「普通にアプリケーションとして使えるレベルにする」となると、 結構めんどくさいことが多々あるんだよなーと、 ぼくも思いました・・・
    • 13.blobstoreの活用方法って、それぐらい?・静的ファイルとして、ではないですが、擬似的にファイルの書き出しが行えるので、 それをうまく活用することはできるかと思います・例えば・・・。 datastoreだと、登録する際には、1エンティティ1MBまでという制約があります・たいていのWebアプリケーションであれば、この制約を気にする機会は あまりないと思うのですが、拙作「sa-boom(サブーン)!!」というWebアプリでは・・・ sa-boom!! : http://sa-boom.appspot.com/
    • 14.iTunes再生回数解析&共有サービス「sa-boom!!」の仕様・仕組み・利用者が自由に指定できる「起点」「終点」のそれぞれの期間で、 iTunes再生回数の差分を取れるアプリケーション。 たとえば「2012年3月1日から2012年3月31日までの再生回数」で ランキング表示したりできる。→ iTunes再生回数の情報は、別途「sa-boom!! client」で  アップロードしておくものとする・内部的には、大雑把にいうと、「曲名-再生回数」の巨大なMapを持っているイメージ・ユーザーがどの期間ごとに差分指定をするかはわからない→ 例え再生回数が1回しかない曲のものでも、  全曲分の情報を保持しておかなくてはいけない(あらかじめ差分期間を想定できたり、そもそも差分を行わないのであれば 上位○曲分の情報だけ持っていればいいけど)
    • 15.blobstore利用までの苦労の変遷・この「sa-boom!!」、実は今年2月にリニューアルしたもの。 これにより、この巨大なMapデータの持ち方も最適化できた・それまでは... i.普通にdatastoreに登録するModelの一属性として持たせる  → ちょっと音楽が好きな人なら、すぐに1MBこえる ii.Mapをserializeしてバイト配列にしたものをzip圧縮、それを持たせる  → ちょっと音楽が好(ry iii.zip圧縮したバイト配列を、 datastoreへの登録が問題ないレベルまで分割。親子関係を持たせて登録  → やっと登録はできるようになった。が、コードがスパゲッティに・今はもうこんな苦労をしなくていい!
    • 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」なので、 実装方法が変わる・いきなり使えなくなるなどのリスクはあるのでご注意
    • 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();       ・・・
    • 16.blobstoreにblobを登録する(3)       ・・・ 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);}・mime-typeを"application/octet-stream"にしてやることにより、 渡されたオブジェクトをバイト配列にしたもの(+zip圧縮)を、 blobstoreにバイナリデータファイルとして書き出せています。と思います。・書き込みが成功するとBlobKeyが得られるのは、画像のアップロードのときと同じ
    • 16.blobstoreにblobを登録する(4)・続いて、blobstoreに書きだしたバイナリデータ(ファイル)を読み込む方法です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();・これで、一手間はかかるけれど、巨大なデータを扱うWebアプリケーションの開発も、gaeで可能!
    • 17.他にどんなことができるの?・GAEアプリケーションは、メールの送信だけでなく、 メールの受信・それをトリガにした処理の実行も可能です!→GAEはメールを受信すると、 「/_ah/mail/メールアドレス」というURL呼び出しに変換する・画像ファイルやhtmlファイルなどの静的ファイルを配置することも可能。→Javaロジックの絡まない、ちょっとしたホームページ作りにもどうぞ! えいのうのいえ: http://a-know-home.appspot.com/・その他、GAEでのアプリ作成を楽しんでみたい!という方へ...
    • 17.他にどんなことができるの?・GAEアプリケーションは、メールの送信だけでなく、 メールの受信・それをトリガにした処理の実行も可能です!→GAEはメールを受信すると、 「/_ah/mail/メールアドレス」というURL呼び出しに変換する・画像ファイルやhtmlファイルなどの静的ファイルを配置することも可能。→Javaロジックの絡まない、ちょっとしたホームページ作りにもどうぞ! えいのうのいえ: http://a-know-home.appspot.com/ 作ればわかる! Google App Engine・その他、GAEでのアプリ作成を楽しんでみたい!という方へ... プログラミング (翔泳社・中垣健志 著)
    • 18.課金体系について(1)・GAEって、こないだのpreview卒業で課金体系が値上げ方向に変わったんでしょう?・GAEって、単純に高いんじゃないの?→そもそも、無料のままでも結構なレベルまで行えますが・・・。 GAEに合ったチューニング・設定をしなければ、そりゃ高くなります! 財布に穴が空いているも同じ!(とはいうぼくも、まだまだザルです)
    • 18.課金体系について(2)・一口に「チューニング」「設定」といっても、高度なものから非常に簡単なものからまで。 まずは「appengine ja night #18で分かった、Google App Engineの 課金の仕組み、節約術・自分用まとめ」(http://d.hatena.ne.jp/a-know/20111126)を見て、 「今すぐにでもできるチューニング・設定」を知りましょう!・最新のガイドも出版されました!ぼくも買いました! 「今のGAE」を、いっしょに勉強しましょう! Google API Expertが解説する Google App Engine for Java実践ガイド (インプレスジャパン・小川 信一(@shin1ogawa) 著)・てか、課金に悩まされるくらいのHitアプリを作ってみたい もんです!(魂の叫び)
    • 19.おわりに・Google App Engine/Java(+ slim3)を使って、 Webアプリケーションによくあるいろんな機能を実現するためのお話でした・正直・・・めんどくさい部分もあります、ロックインしなきゃいけない部分も あります(datastore周りとか)・でも、他のPaaS、IaaSやVPSなどを触ったことがあるわけではないのですが、 これだけ純粋に「アプリケーションの開発」に集中・注力できるのは魅力! (他のサービスにも興味はあるけど)・「作りたい何か」があって、「ひとまずはそれを世に出したい」、 「それを通じて勉強したい」という人にはうってつけのプラットフォームかな、と!・もし、今日おはなしした内容を後日、試されたときに わからないことなどがあれば、いつでも!@a_knowまで、お気軽に! (GAEの場合は、ハッシュタグ#gaeja、#slim3 も、親切な方が多いです!)
    • ご清聴、ありがとうございました!