Google App Script(以下GAS)で、ローカルのファイルをGoogleのサーバー側にアップロードして、何らかの処理をしたいと思うことがあります。

例えばローカルのCSVファイルにGoogle Spreadsheetにインポートするとしてみます。

Web上の日本語情報の多く(例えば、https://qiita.com/ms32/items/7e180c1c1d00fa8df647)は、UiAppクラスを使った例を載せていますが、このクラスは2014年の時点で非推奨になっています(https://developers.google.com/apps-script/reference/ui/ui-app)。

それ以降にもこのUiAppを使って課題を解決している例ばかりなのは問題だと思いますので、GoogleがおすすめするHtmlServiceによる代替策を提示します。

そのために、GASにおけるクライアントとサーバーの連携をまとめてみます。

GASで定義した関数は通常サーバーサイドで実行されます。
これをクライアントサイドで実行するために、ダイアログを作りましょう。
UiAppでは、JavaScriptにより、動的にダイアログを生成していましたが、この程度の役割ならば、htmlファイルを直接書いたほうが早いです。
メニューのツールを押して、Script editorを開いて、次のファイルをindex.htmlという名で保存します。

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <script>
      google.script.run.doSomething();
    </script>
  </body>
</html>

そして、Script editor内に次のmain.gasファイルを作成します。


/**
 * メニューに「Dialog」という項目と、openDialog関数と関連付けられた「Open」というコマンドを追加する。
 * @param e イベント
 */
function onOpen(e) {
  "use strict";
  SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
      .createMenu('Dialog')
      .addItem('Open', 'openDialog')
      .addToUi();
}

/**
 * ダイアログを開く。
 */
function openDialog() {
  "use strict";
  var html = HtmlService.createHtmlOutputFromFile('index');
  SpreadsheetApp.getUi() // DocumentAppやFormApp等目的に合わせたオブジェクトを使う
      .showModalDialog(html, '何かする');
}


/**
 * 何かする関数。
 */
function doSomething() {
  Logger.log("hello");
}

一つ目の関数は、Google Spreadsheetを開いたときのイベントのハンドラーです。
メニューに新しい項目とコマンドを付け加えています。
コマンドには二つ目の関数が紐づけられています。

二つ目の関数は、index.htmlを読み込んで、ダイアログとして、表示しています。

Script editorで定義された関数は、google.script.runというオブジェクトのメソッドとして、登録されます。
その関数を、クライアント側で呼び出すことにより、サーバー側でこの関数が実行されます。
この関数の引数として、クライアント側のデータをサーバー側に送ることができるわけです。

Google Spreadsheetを開きなおすと、メニューに「Dialog」という項目が付け加わっています。
そして「Open」というコマンドを実行して、Script editorのメニューの「View」内の「Log」を見ると「Hello」というメッセージが出力されていることがわかります。

次はScript editorで定義した関数を、フォームのsubmitイベントのハンドラーとして登録することによって、ローカルのデータをサーバー側で処理しましょう。
そのためには、もう一工夫が必要です。
index.htmlを次のように書き換えます。

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <form method="post" enctype="multipart/form-data">
      <label for="file">データ:</label><input type="file" name="file" />
      <input type="submit" value="送信" onclick="google.script.run.withSuccessHandler(function() {google.script.host.close();}).upload(this.parentNode)"/>
    </form>
  </body>
</html>

google.script.hostはgoogle.script.runとは対照的にクライアント側で実行される関数が登録されています。
今回は、このオブジェクトを使って、成功時にダイアログを閉じています。
「流れるようなインターフェース」により、ハンドラーの登録はさらに、google.script.runを返します。
そしてこのオブジェクトに登録された関数uploadを起動します。フォームのDOMオブジェクトを引数に与えます。
gasファイルも書き換えて、uploadを定義します。


/**
 * メニューに「Dialog」という項目と、openDialog関数と関連付けられた「Open」というコマンドを追加する。
 * @param e イベント
 */
function onOpen(e) {
  "use strict";
  SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
      .createMenu('Dialog')
      .addItem('Open', 'openDialog')
      .addToUi();
}

/**
 * ダイアログを開く。
 */
function openDialog() {
  "use strict";
  var html = HtmlService.createHtmlOutputFromFile('index');
  SpreadsheetApp.getUi() // DocumentAppやFormApp等目的に合わせたオブジェクトを使う
      .showModalDialog(html, 'データ送信');
}

/**
 * 受け取ったデータを処理する。
 * @param e イベントオブジェクト。
 */
function upload(e) {
  "use strict";
  Logger.log(e.file.getDataAsString());  
}

ここでメニューの「Open」コマンドをクリックすると、ダイアログが表示されます。
そして適当なテキストファイルを選択して、「送信」をクリックすると、ダイアログが消えます。
そしてLogを確認すると、ファイルの中身が出力されていることがわかります。
このファイルに対して、様々な処理をすることができるわけです。