文学フリマ版電書サーバー
前回述べたように、ホストをGoogle App ServerからHerokuに移行し、Heroku移行後はさまざまな要望に応じて機能を追加しました。今回は、文学フリマ版電書サーバーでは、どのような考えで新機能を追加していったのかを解説します。
sinatraによるアプリ開発
sinatraはDSLによるWebプログラミングを可能にするライブラリです。Ruby on Railsに比較すると、小フットプリント、軽量、シンプルなプログラミングなどが特徴です。
例えば、「ドキュメントルートにアクセスがあればindex.erbを表示する」という場合は、以下のように記述します。
getがDSLです。Railsでいうroutesとコントローラが一体になった感じですね。ただし、Railsにあるような、ActiveRecordからFormHelperまできれいに連携するような機能はありません。
また、電書サーバーを構築した経験から言えば、十画面ぐらいまでのWebアプリではsinatraのシンプルさが生きます。これを越えた場合は、全体の構造を把握するのが難しくなってくるため、Railsなどに移行を考えるべきでしょう。
販売画面
認証
販売画面のURLが広まってしまうと、自由に電書をダウンロードされてしまいます。そこで簡単な認証を導入しました。サイトを使うのは電書部のメンバーだけなので、あまり凝った仕掛けは必要ありません。sinatraのBASIC認証プラグインsinatra-authorization gemを使いました。インストールして、以下の2つのメソッドを定義しします。
authorizeメソッドでは、有効なidとパスワードの組み合わせでtrueが返るようにします。あとは認証が必要なところに「login_required」を記述するだけでBASIC認証が使えるようになります。
合計金額表示
電書を選択中に合計金額を表示するにはJavaScriptを使っています。実際の販売では、注文をきき、電書を選び(ここで合計を表示)、価格を告げ、サーバーに注文を送り、お金をもらう、という流れになっていたようです。
まとめ買い
さらに販促のツールとして「まとめ買い」を導入しました。「まとめ買い」は、一度に10冊以上購入すると電書が一冊100円になるというものです。割引ではなく一冊100円なのは、電書の作者のみなさんへのお金の分配を簡単にするためです。
まとめ買いは、経済的に得するのはもちろんですが、「何冊買ってもかさばらないし、重くもない」という電子書籍の特徴をフルに生かしたサービスになっていることに、後から気づきました。まとめて全部購入された方は非常に多かったです。文学フリマ当日は雨模様でしたからなおさらです。
電書ナンバー
前回も触れた販売シミュレーションで判明した、メールアドレス入力時のトラブル(入力ミスや手間)の対策として、「電書ナンバー」を導入しました。そして、あらかじめ誰でもアクセスできる電書ナンバー画面を用意しました。ここでメールアドレスを入力してもらい、1~3桁の数字を発行します。これがメールアドレスの代わりになります。
販売画面では、電書ナンバーからメールアドレスをひく機能を追加したので、メールアドレスの代わりに電書ナンバーですむようになりました。ただ、電書ナンバーを活用するには、前もって登録してもらう必要があるので、事前の広報活動が重要になります。
メアド埋め込み
電書の注文がサーバーに送信されると、電書のマスターデータを取り出して、購入者のメールアドレスを埋め込み、購入者用の電書を生成します。heroku上にはファイルは置けないため、電書には一意のIDを割り振って、AmazonのストレージサービスであるS3に保存しています。
メール送信
前回述べたように、メール送信にはSendgridをつかっています。Sendgridはプラグインで、free、pro、premiumの3つのバージョンがあり、違いは送信できる通数と料金です。herokuコマンドで簡単に切り替えられるため、開発中はfree(無料のfreeでも200通/日まで送信できます)、本番ではproを使いました。同じ設定がすべてのバージョンで使え、freeからproに切り替えても設定を変える必要がないことを重要視しました。本番でトラブルは困りますから。
ダウンロード
販売した電書はAmazon S3に保存されています。S3から直接ダウンロードするようにした場合、そのURLが広まれば誰でもダウンロードできてしまいます。そこで、一度電書サーバー側でリクエストを受けて、電書サーバーがS3からダウンロード、そのデータをレスポンスとして返すようにしています。
この方法の利点は、ダウンロード期間や回数を電書サーバーで管理できることです。
ダウンロードのURL
ダウンロードのURLは購入者毎に異なります。しかし、「http://densho.heroku.com/dl/epub/1/sample_book」のように単純に数字のidで区別すると、id部分だけ書き換えて他の人の電書がダウンロードされてしまうかもしれません。これを防ぐため、数値ではなくランダムな英数字を含めるようにしています。URLは「http://densho.heroku.com/dl/epub/5CBV7N/sample_book」のようになります。
電書管理
電書のマスターデータはPDFとEPUBがあります。さらにPDFにはiPhoneなどの画面の小さいデバイス用とPC用があります。開発当初は、すべて電書サーバー上にありました。herokuでファイルは作れませんが、サーバの一部としてデプロイするのは問題ありません。
しかし、最初に想定していた「身内の電書を2冊ぐらい出す」という構想はどんどん膨れ上がり、プロの書き手の作品を含む15作品になりました。こうなると、電書サーバーの容量が数十MBになります。herokuではWebアプリの容量(スラグ・サイズといいます)が100MBに制限されています。スラグ・サイズが大きくなると起動にも時間がかかります。
そこで、PDFのマスターデータをS3に移すことにしました。S3のアクセスにはs3 gemを使っています。ただ、終わってから調べてみるとAWS::S3の方がより使いやすそうです。
ちなみに通常S3はデータを転送すると課金されますが、herokuはAmazon EC2上に構築されているため、いくら転送しても課金はされませんでした。
電書登録
PDFをS3にコピー、EPUBをWebアプリの一部としてデプロイしたら、電書の情報を登録します。電書情報は、タイトル、作者名、価格、提供する形式(PDFのみ、EPUBのみも可)などです。このデータはherokuから使えるRDB(postgresql)に保存されます。電書情報とEPUBとPDFを紐づけるデータはファイル名だけです。これは人手で合わせるしかないため、気をつかいました。
売上管理
売上は「販売履歴」と「購入者一覧」の2つの画面で見ることができます。
2つとも購入アクションごとに生成されるエントリ(RDBに保存されます)を時系列に並べて表示するものです。いつ誰が買ったかを確認できます。
「販売履歴」は電書ごとの総売上と、個々の電書の購入エントリをシンプルに時系列に並べたものです。ダウンロード時刻や回数も表示します。
「購入者一覧」は、購入者毎に電書をまとめて表示します。
結果
文学フリマ当日は、朝のうちにherokuのワーカ(起動するWebアプリのインスタンス)を1から2に増やし、メールプラグインをSendgrid proに入れ換えました。サーバに異常なく稼働していることを確認して、正午頃に文学フリマ会場に到着しました。会場は大変なことになっていました。電書部の机の前には行列ができていたのです。
結局、電書フリマでは1,400冊以上の電書を売り上げました。はっきりいって予想を完全に越えていました。ずいぶん行列ができてしまい、周囲のサークルにも迷惑をおかけしたのではないかと思います。申し訳ありません。文学フリマの紙の本は持ち込む量に限界がありますから、1,400冊というのはもしかすると文学フリマ記録かもしれません。
電書フリマに向かって
文学フリマの大成功を受けて、次は電子書籍だけを販売する「電書フリマ」構想が持ち上がりました。すでに文学フリマ版の反省点と、電書フリマでやりたいことがいくつか見えてました。
タイムアウト問題
PDFをS3に移した結果、残念ながら再びタイムアウトが起きるようになりました。S3は日本から使うとレスポンスが余りよくないのが原因だと思われます。その対策として、文学フリマ版ではPDFをやむなく分割しましたが、本来ならあまりやりたくありませんでした。そこで電書フリマ版では、分割もせずタイムアウトもしない工夫をする必要がありました。
販売拠点の展開
同じ電書を別々の場所で売りたい。大げさに言えば、すべての都道府県に電書フリマの拠点を置いて同時開催したいという壮大な構想です。技術的にはマルチユーザー対応でしょうか。
iPhone対応
iPhoneに対応して、iPhoneで販売したい。iPhoneにはSafariがあるので簡単にできると思いました。甘かったです。
次回以降は、しばらくサーバーの話は少しお休みして、電書変換(テキストからPDFやEPUBに変換する)について説明します。