式の余興に「AWS+GASシステム」で超盛り上がった件 〜①画面編〜の続きになります。
今回の記事は主に、バックエンド周り(GAS)の実装に焦点を当てています。
Google App Script
このシステムのメイン機能はすべてGASが担当しています。
無料でここまでできるなんて、すごいぞGoogle!
サンプルコード
※以下のプログラム内の様々なIDはXXXXXXXXXXXXX
に置き換えています
/** 画像格納フォルダID */
const FOLDER_ID = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
/** スプレッドシートID */
const SHEET_ID = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
const A = 'A';
const B = 'B';
const C = 'C';
const D = 'D';
/** WebアプリケーションへのGETリクエストを処理する */
function doGet(e) {
// HTMLテンプレートファイルを読み込み
const template = HtmlService.createTemplateFromFile('src/Index');
// HTMLを評価して出力する
const htmlOutput = template.evaluate();
// iFrame埋め込みを許可
htmlOutput.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
// メタタグの追加(レスポンシブデザイン対応)
htmlOutput.addMetaTag('viewport', 'width=device-width, initial-scale=1');
htmlOutput.addMetaTag('apple-mobile-web-app-capable', 'yes');
htmlOutput.addMetaTag('mobile-web-app-capable', 'yes');
return htmlOutput;
}
/** WebアプリケーションへのPOSTリクエストを処理する */
function doPost(e) {
// 画像が選択されているかチェック
if (e.parameters.image == '') {
throw 'ファイルが選択されていません。';
}
// 画像データのデコード
const imageData = Utilities.base64Decode(e.parameter.himage, Utilities.Charset.UTF_8);
// Blobとしてファイルを作成
const fileBlob = Utilities.newBlob(imageData, 'image/jpg', e.parameter.image);
// 画像ファイルをドライブにアップロード
const file = DriveApp.getFolderById(FOLDER_ID).createFile(fileBlob);
// スプレッドシートに画像と日付を追加
createSheet();
// AIサーバに画像を送信し、データをシートに挿入
insertToSheet(file, fileBlob);
return;
}
/** スプレッドシートへのデータ挿入処理 */
function insertToSheet(file, blob) {
// エンドポイントURLの取得
const url = getEndpoint();
// 現在の日付の取得
const today = getDate();
// スプレッドシートの該当シートを取得
const mySheet = SpreadsheetApp.openById(SHEET_ID).getSheetByName(today);
mySheet.activate();
// シートの最終行を取得
const lastRow = mySheet.getLastRow() + 1;
// 日時、ファイルURL、ファイルの公開URL、笑顔指数をシートに記録
mySheet.getRange(A + lastRow).setValue(getDateTime());
mySheet.getRange(B + lastRow).setValue(file.getUrl());
mySheet
.getRange(C + lastRow)
.setValue('https://lh3.googleusercontent.com/d/' + file.getId());
const smile = fetchSmile(blob, url);
mySheet.getRange(D + lastRow).setValue(smile);
if (smile != '') {
const rate = JSON.parse(smile);
mySheet.getRange('E' + lastRow).setValue(rate[0][2]);
}
}
/** エンドポイントURLの取得 */
function getEndpoint() {
const mySheet = SpreadsheetApp.openById(SHEET_ID).getSheetByName('設定');
const endpoint = mySheet.getRange(B + 1).getValue();
return endpoint;
}
/** エンドポイントへのデータ送信と結果の取得 */
function fetchSmile(blob, url) {
const params = {
method: 'post',
payload: {
upload_file: blob
}
};
const response = UrlFetchApp.fetch(url, params).getContentText();
return response;
}
/** 必要に応じてスプレッドシートの新しいシートを作成 */
function createSheet() {
const today = getDate();
const ss = SpreadsheetApp.openById(SHEET_ID).getSheetByName(today);
if (!ss) {
const newSheet = SpreadsheetApp.openById(SHEET_ID).insertSheet();
newSheet.setName(today);
}
}
/** 日付の文字列形式を取得(年月日) */
function getDate() {
const today = Utilities.formatDate(new Date(), 'JST', 'yyyyMMdd');
return today;
}
/** 日付と時間の文字列形式を取得(年月日時分秒) */
function getDateTime() {
const today = Utilities.formatDate(new Date(), 'JST', 'yyyy/MM/dd hh:mm:ss');
return today;
}
/** スプレッドシートID */
var SHEET_ID = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
/** HTTP GETリクエストを受け取り、スプレッドシートのデータをJSON形式で返す */
function doGet(e) {
// JSON形式でレスポンスを生成
var output = ContentService.createTextOutput(JSON.stringify(getJSONFromSheet()));
// レスポンスのMIMEタイプをJSONに設定
output.setMimeType(ContentService.MimeType.JSON);
// JSON形式のレスポンスを返す
return output;
}
/** スプレッドシートからデータを取得し、JSON形式で整形する */
function getJSONFromSheet() {
// スプレッドシートを開く
var ss = SpreadsheetApp.openById(SHEET_ID);
// 本日の日付に対応するシートを取得
var sheet = ss.getSheetByName(getDate());
// 第1列目から3列目、1行目から最終行までのデータを取得
var values = sheet.getRange(1, 3, sheet.getLastRow(), 2).getValues();
// 結果を格納する配列を初期化
var res = new Array(sheet.getLastRow());
// 取得したデータをJSONオブジェクトに変換
for (var i = 0; i < sheet.getLastRow(); i++) {
res[i] = {
url: values[i][0],
smile: values[i][1]
};
}
// 変換したデータをコンソールに出力(デバッグ用)
console.log(res);
// JSONオブジェクトを返す
return res;
}
/** 文字列形式の日付を作成 */
function getDate() {
// 現在の日付を 'yyyyMMdd' 形式で取得
var today = Utilities.formatDate(new Date(), "JST", "yyyyMMdd");
return today;
}
/** スプレッドシートID */
var SHEET_ID = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
/** WebアプリケーションへのGETリクエストを処理し、JSON形式でレスポンスを返す */
function doGet(e) {
// スプレッドシートのデータをソート
sort();
// JSON形式でレスポンスを生成
var output = ContentService.createTextOutput(JSON.stringify(getJSONFromSheet()));
// レスポンスのMIMEタイプをJSONに設定
output.setMimeType(ContentService.MimeType.JSON);
// JSON形式のレスポンスを返す
return output;
}
/** スプレッドシートからデータを取得し、JSON形式で整形する */
function getJSONFromSheet() {
// スプレッドシートを開く
var ss = SpreadsheetApp.openById(SHEET_ID);
// 本日の日付に対応するシートを取得
var sheet = ss.getSheetByName(getDate());
// 第1行目から3列目、1行目から2列分のデータを取得
var values = sheet.getRange(1, 3, 1, 2).getValues();
// 取得したデータをJSONオブジェクトに変換
var res = [
{
url: values[0][0],
smile: values[0][1]
},
];
// 変換したデータをコンソールに出力(デバッグ用)
console.log(res);
// JSONオブジェクトを返す
return res;
}
/** スプレッドシートのデータをソートする */
function sort() {
// スプレッドシートを開く
var ss = SpreadsheetApp.openById(SHEET_ID);
// 本日の日付に対応するシートを取得
var sheet = ss.getSheetByName(getDate());
// ソートする範囲を指定(第1行目から最終行まで、5列分)
var data = sheet.getRange(1, 1, sheet.getLastRow(), 5);
// 第5列(列E)を基準に降順でソート
data.sort({ column: 5, ascending: false });
}
/** 文字列形式の日付を作成 */
function getDate() {
// 現在の日付を 'yyyyMMdd' 形式で取得
var today = Utilities.formatDate(new Date(), "JST", "yyyyMMdd");
return today;
}
このGASは大きく3つの役割を持っていて、それぞれファイルへ分けています。
- スマホから投稿を行う処理
- スクリーンから定期的に呼ばれる一覧取得処理
- 結果発表処理
①スマホから投稿を行う処理(Gas-smileys-main)
スマホから画像が送られてくるので、その画像をGoogleドライブへ保存します。
同時に保存された画面をGoogleスプレッドシートへログ形式で書き込んでいきます。
Googleドライブから取得した文字列をB列へ、HTMLに埋め込み可能URLをC列へ格納します。
※以下はGoogleスプレッドシートのイメージ(この時点では笑顔数値であるD列/E列は空です)
そして後述するAWS上に構築した笑顔数値APIへフックし、取得した笑顔数値を書き込んで処理は完了します。
ちなみに
AWSのエンドポイントURLは、EC2インスタンスを起動する度に変更されるため、スプレッドシートから読み取る仕様にしました。
※EC2インスタンス起動のたびに、GASを再デプロイするのが面倒だったので。
これ気づくのが終盤で、EC2起動のたび大変だった。。。早めに気づけばよかった...
②スクリーンから定期的に呼ばれる一覧取得処理(Gas-smileys-back)
前回記事で詳しく説明しましたが、会場内に表示したスクリーン画面(JavaScript)は5秒ごとにポーリングしています。
そのポーリング先として、本処理が呼び出されることになります。
処理はものすごく単純で、Googleスプレッドシートの一覧を全件取得してJSON形式で返却するといったものです。
※一覧の件数が肥大すると処理に時間がかかると思われるので、5秒という時間では間に合わなくなってくるかもです
③結果発表処理(Gas-smileys-end)
スクリーン画面のENDボタンを押すことで、最も笑顔数値が高い写真が表示する必要があります。
Googleスプレッドシートにて笑顔数値(E列)を降順にソートし、一番上にある行を取得し返却するということを行なっています。
あとがき
次回はいよいよAWS周りについてお話ししていきます。
このサービスを一通り作って思ったのですが、GASではなくすべてAWSで作成したらもっと楽できた気がします。
(来月AWS Certified Cloud Practitioner認定を受験してきます。頑張ります)