[GAS] アップローダを使用してスプレッドシートへCSVを展開する～V8ランタイム暫定対応

## はじめに
最近、立て続けにＣＳＶアップローダを絡めた案件依頼がありまして、その度に**ＧＡＳ側のバグのため、古い実行環境で運用する必要があります**、というような説明をしていました。もちろん、代替案として**ドライブ経由のアップロード**についても説明し、選択していただくようにしていました。ただ、この対応は思考することを放棄しているようで、なんとなくスッキリしませんでした。そこで、なにか対応策はないものかと、調べてみました。

### V8ランタイムとRhino
執筆時点でのGAS開発環境のデフォルトはV8ランタイムとなっています。それ以前はMozillaのRhino（ライノ）というエンジンで動いていたようですが、この移行により、モダンなJavascript（一部を除きES2019まで）の記法や関数が使えるようになりました。それと同時に、処理速度も向上しているようです。

しかしながら、エンジン移行に伴う影響も多々報告されています（多くは過去資産が動かなくなったというものですが）。これらについては[officeの杜さんの記事](https://officeforest.org/wp/2020/02/06/google-apps-script%E3%81%AEv8-runtime%E5%AF%BE%E5%BF%9C%E3%82%92%E6%A4%9C%E8%A8%BC%E3%81%97%E3%81%A6%E3%81%BF%E3%81%9F/)が非常に参考になると思います。[^1]

その影響の一つに、本記事のメインテーマ、アップローダが機能しない、というものがあります。具体的には、HtmlServiceにて生成したフォーム経由でアップロードを実行した場合、ファイル送信処理のままになる、バイナリデータが壊れる、というものです。テキストファイルなら大丈夫という報告も散見されますが、少なくとも私のコードではＧＡＳ側で受け取ることはできませんでした。

[^1]: 執筆時点から１年以上前の情報となりますので、既に対応がされている部分もあります。

### V8ランタイム環境で運用できることが最善
暫定的にV8ランタイムからRhino環境に切り替えられるようになっていますが、いずれ廃止されるであろうことを考慮すると、いつまでもRhino環境で運用していただくわけにはいきません。V8ランタイム環境で運用できることが最善、ということで、バイト配列（型付き配列）[^2] に変換してからＧＡＳへ送信するという[回避策](https://www.javaer101.com/en/article/51516102.html)がありましたので実装、検証してみました。

[^2]: バイト配列（型付き配列）の詳細は[ＭＤＮ](https://developer.mozilla.org/ja/docs/Web/JavaScript/Typed_arrays)にてご確認ください。

## 目的
V8ランタイム環境下にて動作するＣＳＶアップローダを実装します。ただし、今後対応される可能性も高いため、あくまでも暫定対応という位置づけです。

## ロジック
ＣＳＶを展開するシートをアクティブにした状態で、カスタムメニューによる実行を前提としています。

先述した記事を参考にし、フォーム部分のＨＴＭＬを記述します。

```HTML:sample.html
<form id='myForm'>
  <input name="myFile" type="file">
  <button onclick="fUpload(this.parentNode)">UPLOAD</button> 
</form>
<div id='message'></div>
<script>
  // UPLOAD処理
  const fUpload = fObject => {
    const raw = fObject.myFile.files[0]
    const fr  = new FileReader()
    fr.onload = e => {
      const obj = {
        mimeType: raw.type,
        bytes: [...new Int8Array(e.target.result)] // 受け取ったデータをバイト配列へ
      }
      google.script.run
      .withSuccessHandler(onSuccess)
      .withFailureHandler(onFailure)
      .stUploaderV8(obj)
    }
    fr.readAsArrayBuffer(raw)
    document.querySelector('#myForm').style.display = 'none'
    document.querySelector('#message').innerHTML = 'UPLOAD...'
  }
      
  // 成功時処理
  function onSuccess() {
    google.script.host.close()
  }

  // 失敗時処理
  function onFailure(e) {
    document.querySelector('#message').innerHTML = `ERROR! ${e.message}`
  }
</script>
```

アップロードしたＣＳＶデータをクライアントサイドでバイト配列へ変換し、ＧＡＳに渡します。`[...new Int8Array(e.target.result)]`のように、クライアントサイド側ならRhino環境でもスプレッド構文などが使えます。

下記、受け取り側のＧＡＳコードです。

```Javascript:main.gs
function stUploaderV8(fObject) {
  const csvData = Utilities.newBlob(fObject.bytes, fObject.mimeType).getDataAsString() // バイト配列として受け取ったデータをCSVとして取得
  const parsed  = Utilities.parseCsv(csvData)// 二次元配列へ

  // CSVをアクティブシートへセット
  const Sh = SpreadsheetApp.getActive().getActiveSheet()

  /* 以降の処理省略 */
}
```

バイト配列を`newBlob()`経由でＣＳＶ変換し、`perseCsv()`で二次元配列に変換します。二次元配列へ展開できたら後はシートへセットするだけですので、以降の処理については割愛します。

## 最後に
V8ランタイムではHtmlService経由のフォームオブジェクトをうまく解析できないようで、事前に解析しやすいようにした上でＧＡＳへ渡す、ということなのでしょうね。

表現として合っているかどうかは不明ですが、薬で言えば、糖衣錠やカプセルタイプのものではなく、粉状やドリンクタイプ、点滴等に代えて対処しなければならないということでしょうね。

### 余談
他者様が構築したプロジェクトのリファクタリングや修正を行う機会もあり、そのときに遭遇したのが、Rhino環境下でアップローダが機能しなくなったケース。ＮＤＡのため詳しくお伝えできませんが、ＧＡＳ側のＣＳＶ→二次元配列処理中に止まっていました。

### [参考]
+ [Google Apps ScriptのV8 Runtime対応を検証してみた officeの杜](https://officeforest.org/wp/2020/02/06/google-apps-script%E3%81%AEv8-runtime%E5%AF%BE%E5%BF%9C%E3%82%92%E6%A4%9C%E8%A8%BC%E3%81%97%E3%81%A6%E3%81%BF%E3%81%9F/)
+ [Javaer101: Google Apps Scriptをv8ファイルアップロードに移動すると、サイドバーから機能しなくなりました](https://www.javaer101.com/en/article/51516102.html)
+ [JavaScript 型付き配列 - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Typed_arrays)