Google App Script(GAS)では、指定したHTMLでスプレッドシート上にモーダル(ダイアログ)を表示することができます。
この機能を使うと、モーダル(ダイアログ)を表示してその中にダウンロードボタンを設置し、ボタンクリックで対象のスプレッドシートをダウンロードすることができます。
ここではその方法を実例で解説しています。
どんなことができるか?
例えば、スプレッドシート上に「プログラム」とうメニューを追加して、その中の「ファイルダウンロード」をクリックします。
するとダウンロードボタンがあるダイアログが開きます。
ボタンをクリックすると指定したファイル形式で現在のスプレッドシートをダウンロードすることができます。
GASを使えばこのようなことができます。
HTMLを指定してダイアログを作成する方法
GASでHTMLを指定してダイアログを作成するには、HTMLファイルとそれを呼び出す.gsファイルを作成する必要があります。
今回作成するのは以下の2つのファイルです。
それぞれのファイルのコードは以下になります。
function onOpen() {
const ui = SpreadsheetApp.getUi();
const menu = ui.createMenu('プログラム');
menu.addItem('ファイルダウンロード', 'modalDownload');
menu.addToUi();
}
function modalDownload() {
const html = HtmlService.createHtmlOutputFromFile("index");
SpreadsheetApp.getUi().showModalDialog(html, "ダウンロードボタン");
}
function createDataUrl(type) {
const mimeTypes = { csv: MimeType.MICROSOFT_EXCEL, pdf: MimeType.PDF };
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getActiveSheet();
let url = null;
if (type == "csv" || type == "txt") {
url = `https://docs.google.com/spreadsheets/d/${ss.getId()}/gviz/tq?tqx=out:csv&gid=${sheet.getSheetId()}`;
} else if (type == "pdf") {
url = `https://docs.google.com/spreadsheets/d/${ss.getId()}/export?format=pdf&gid=${sheet.getSheetId()}`;
}
if (url) {
const blob = UrlFetchApp.fetch(url, {
headers: { authorization: `Bearer ${ScriptApp.getOAuthToken()}` },
}).getBlob();
return {
url: url,
data:
`data:${mimeTypes[type]};base64,` +
Utilities.base64Encode(blob.getBytes()),
filename: `${sheet.getSheetName()}.${type}`,
};
}
return { data: null, filename: null };
}
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<style>
input {
border-radius: 50%;
line-height: 100px;
width: 100px;
height: 100px;
padding: 0 5px 0;
margin: 15px;
background: #f0e0e0;
-webkit-box-shadow: 0 5px 0 #f0e0e0;
box-shadow: 0 5px 0 #f0e0e0;
}
input:hover {
-webkit-transform: translate(0, 3px);
transform: translate(0, 3px);
-webkit-box-shadow: 0 2px 0 #f0e0e0;
box-shadow: 0 2px 0 #f0e0e0;
}
</style>
</head>
<body>
<h1 align="center">ダウンロード</h1>
<div align="center">
<input type="button" value="CSV" onclick="download('csv')" />
<input type="button" value="PDF" onclick="download('pdf')" />
<input type="button" value="TXT" onclick="download('txt')" />
</div>
</body>
<script>
function download(type) {
google.script.run
.withSuccessHandler(({ data, filename }) => {
if (data && filename) {
const a = document.createElement("a");
document.body.appendChild(a);
a.download = filename;
a.href = data;
a.click();
}
})
.createDataUrl(type);
}
</script>
</html>
メニューを追加する方法
スプレッドシートにメニューを追加する方法についてはここでは割愛します。下記をご参考ください。
(参考)【GAS】メニューに自作した関数/プログラムを追加する方法を実例で解説
モーダルを表示する方法|showModalDialog
スプレッドシート上にモーダルを表示するにはshowModalDialogメソッドを使います。
基本構文は以下のようになります。
ユーザーインターフェース.showModelessDialog(HtmlOutputオブジェクト, タイトル)
ユーザーインターフェースとは、スプレッドシートであれば「SpreadsheetApp.getUi()」とします。
HtmlOutputオブジェクトとは、htmlファイルをオブジェクト化したものです。
実例としては以下のように使います。
function modalDownload() {
const html = HtmlService.createHtmlOutputFromFile("index");
SpreadsheetApp.getUi().showModalDialog(html, "ダウンロードボタン");
}
変数「html」がHtmlOutputオブジェクトです。
なお、タイトルはモーダルの左上に表示される文字列です。
(参考)AppScript > showModelessDialog
コードの解説
showModalDialog以外のコードは以下のようになっています。
HtmlService
一番最初の「HtmlService」とはAppScriptが提供しているクラスで、スクリプトによってHTMLなどのテキストコンテンツを作成するものです。
HtmlService.関数名()
createHtmlOutputFromFile
createHtmlOutputFromFileはHtmlServiceクラスに用意されたメソッドです。
HtmlService.createHtmlOutputFromFile(HTMLファイル名)
引数にhtmlファイル名を指定することで、指定したファイルの内容のオブジェクト(HtmlOutputオブジェクト)を作成します。
指定したファイルのHTML形式が正しくない場合はエラーを返します。
(参考)Class HtmlService: createHtmlOutputFromFile(filename)
以上で、モーダルとして表示するHTMLファイルのオブジェクトの取得が完了です。
SpreadsheetApp
次に、スプレッドシートにサイドバーを実装するコードを見ていきます。
SpreadsheetApp.getUi().showSidebar(html);
一番最初に出てくる「SpreadsheetApp」はGASでスプレッドシートを操作するためのクラスです。様々な便利なメソッドが用意されています。
getUi
getUiメソッドはSpreadsheetAppクラスに用意されたメソッドで、ユーザーインターフェースのインスタンスを取得します。
これで実際に表示されているスプレッドシートの画面を操作することがっできるようになります。引数は不要です。
ダウンロード処理
続いて実際のダウンロード処理の流れについて解説します。
ダイアログでHTMLファイルを表示する
スプレッドシート上で「ファイルダウンロード」というメニューをクリックすると「modalDownload」という関数を実行します。
function modalDownload() {
const html = HtmlService.createHtmlOutputFromFile("index");
SpreadsheetApp.getUi().showModalDialog(html, "ダウンロードボタン");
}
これでモーダルとして指定したHTMLファイルの内容を表示します。
onclickイベント
ボタンをクリックするとHTMLファイルで割り当てられているonclickイベントが発動します。
<input type="button" value="CSV" onclick="download('csv')" />
<input type="button" value="PDF" onclick="download('pdf')" />
<input type="button" value="TXT" onclick="download('txt')" />
inputの属性の1つに「onclick」を設定することで、このタグに対してonclickのイベントハンドラを設置することができます。
実行する関数名と渡す引数を指定します。
onclick="実行する関数名(引数)"
例えば、以下のような記述があるとします。
<input type="button" value="CSV" onclick="download('csv')" />
この場合、CSVと書かれたinputボタンをクリックすると、downloadという関数を実行し、その関数にcsvという文字列を引数として渡します。
scriptタグ、download関数
HTML内にscriptタグを使って実行するdownload関数の処理を記述します。
<script>
function download(type) {
google.script.run
.withSuccessHandler(({ data, filename }) => {
if (data && filename) {
const a = document.createElement("a");
document.body.appendChild(a);
a.download = filename;
a.href = data;
a.click();
}
})
.createDataUrl(type);
}
</script>
function download(type)
とすることで、渡された引数の値をtypeという変数に格納します。
CSVボタンがクリックされた場合はtype="csv"
、TXTボタンがクリックされた場合はtype="txt"
となります。
ここの処理は結構肝なので、下の方に切り出して詳細を解説します。
createDataUrl関数
download関数の処理の中で「createDataUrl関数」も実行しています。
.createDataUrl(type);
これは、AppScriptに記述してある関数です。
function createDataUrl(type) {
const mimeTypes = { csv: MimeType.MICROSOFT_EXCEL, pdf: MimeType.PDF };
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getActiveSheet();
let url = null;
if (type == "csv" || type == "txt") {
url = `https://docs.google.com/spreadsheets/d/${ss.getId()}/gviz/tq?tqx=out:csv&gid=${sheet.getSheetId()}`;
} else if (type == "pdf") {
url = `https://docs.google.com/spreadsheets/d/${ss.getId()}/export?format=pdf&gid=${sheet.getSheetId()}`;
}
if (url) {
const blob = UrlFetchApp.fetch(url, {
headers: { authorization: `Bearer ${ScriptApp.getOAuthToken()}` },
}).getBlob();
return {
url: url,
data:
`data:${mimeTypes[type]};base64,` +
Utilities.base64Encode(blob.getBytes()),
filename: `${sheet.getSheetName()}.${type}`,
};
}
return { data: null, filename: null };
}
この処理を実行することで、スプレッドシートをCSVに変換してダウンロードするためのリンクを生成します。
戻り値の部分は以下になります。
return {
data:
`data:${mimeTypes[type]};base64,` +
Utilities.base64Encode(blob.getBytes()),
filename: `${sheet.getSheetName()}.${type}`,
};
オブジェクト形式でdataプロパティの値にダウンロード用のURL、filemameプロパティの値にシート名.typeというファイル名を指定しています。
この戻り値がgoogle.script.run.withSuccessHandler
に渡されます。
.withSuccessHandler(({ data, filename }) => {
if (data && filename) {
const a = document.createElement("a");
document.body.appendChild(a);
a.download = filename;
a.href = data;
a.click();
}
})
aタグにファイルダウンロードの名前としてdownload=filenameという属性を追加し、ダウンロードリンクとしてhref=dataという属性を追加しています。
最後に、clickイベントを使って自動ダウンロードを行います。
scriptタグの中身
今回の処理で肝となるscriptタグの中身についてもう少し詳しく解説していきます。
<script>
function download(type) {
google.script.run
.withSuccessHandler(({ data, filename }) => {
if (data && filename) {
const a = document.createElement("a");
document.body.appendChild(a);
a.download = filename;
a.href = data;
a.click();
}
})
.createDataUrl(type);
}
</script>
google.script.runとは何か?
onlickイベントで作動する処理の一番最初にgoogle.script.run
があります。
これはAppScriptで作成したHTMLファイルの中でAppScriptを実行できるとても便利な処理です。
App Scriptではファイルの種類として「スクリプト」と「HTML」を作成することができます。
このHTMLファイルの中で使う処理になります。
(参考)クラス google.script.run(クライアントサイド API)
withSuccessHandler
withSuccessHandlerはgoogle.script.runクラスの中で使えるメソッドです。
以下のように使用します。
google.script.run.withSuccessHandler(関数).先に実行する関数
withSuccessHandlerを使うと、後ろに指定したサーバーサイド関数が正常に返された場合に実行するコールバック関数を設定することができます。
簡単に言うと、先に用意されている関数がエラーなく実行されたら次にこの処理を行うということです。
今回使用しているコードをざっくりまとめると以下になります。
google.script.run
.withSuccessHandler(処理)
.createDataUrl(type);
つまり、先にcreateDataUrl(type)を実行し、その結果を受けてwithSuccessHandlerの処理を行うという処理になります。
(参考)クラス google.script.run: withSuccessHandler
createDataUrl(type)|CSVとPDFダウンロード用のURLを作成する
コード全体像
withSuccessHandlerで指定した関数を実行する前に実行しているのがcreateDataUrl(type)
です。
<script>
function download(type) {
google.script.run
.withSuccessHandler()
.createDataUrl(type);
}
</script>
download関数で渡された引数を渡しています。
この関数はappscriptの方に記載されています。この処理でCSVとPDFダウンロード用のURLを作成します。
function createDataUrl(type) {
const mimeTypes = { csv: MimeType.MICROSOFT_EXCEL, pdf: MimeType.PDF };
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getActiveSheet();
let url = null;
if (type == "csv" || type == "txt") {
url = `https://docs.google.com/spreadsheets/d/${ss.getId()}/gviz/tq?tqx=out:csv&gid=${sheet.getSheetId()}`;
} else if (type == "pdf") {
url = `https://docs.google.com/spreadsheets/d/${ss.getId()}/export?format=pdf&gid=${sheet.getSheetId()}`;
}
if (url) {
const blob = UrlFetchApp.fetch(url, {
headers: { authorization: `Bearer ${ScriptApp.getOAuthToken()}` },
}).getBlob();
return {
url: url,
data:
`data:${mimeTypes[type]};base64,` +
Utilities.base64Encode(blob.getBytes()),
filename: `${sheet.getSheetName()}.${type}`,
};
}
return { data: null, filename: null };
}
CSVダウンロードURLの作成
スプレッドシートのURLを作成
url = `https://docs.google.com/spreadsheets/d/${ss.getId()}/gviz/tq?tqx=out:csv&gid=${sheet.getSheetId()}`;
まず、スプレッドシートをCSVとしてダウンロードするためのURLを生成してます。
https://docs.google.com/spreadsheets/d/${ss.getId()}
スプレッドシートのオブジェクトに対してgetIdを使用することでシートのID番号を取得しています。
gvizライブラリを使用|gvizとは何か?
/gviz/tq?tqx=out:csv&gid=${sheet.getSheetId()}`;
続いて「gviz」という表記を入れています。これは「Google Visualization API」というAPIを使用するという指示です。
後ろのtq?以降でどのように使用するかを指定しています。
tqx=out:csv&gid=${sheet.getSheetId()}`;
上記のようにtqxを使用して「out:csv」を指定するとCSVファイルをダウンロードすることができます。
PDFダウンロードURLの作成
https://docs.google.com/spreadsheets/d/${ss.getId()}/export?format=pdf&gid=${sheet.getSheetId()}
スプレッドシートをPDFに変換して保存するためのURLを作成しています。
export?format=pdf&gid=${sheet.getSheetId()}
スプレッドシートを指定したあとにexport?format=pdf
と指定し、アンパサンド(&)で対象のシートのIDを指定すれば、そのシートをPDFとしてダウンロードするためのリンクを生成します。
なお、このURLにパラメータを付け足すことでPDFの向きやマージンなどを設定することもできます。
(参考)Google スプレッドシートから PDF を生成して送信
Base64への変換(エンコード)
Base64とは何か?
上記で作成したダウンロード用のURLをクリックするだけでもダウンロードできます。
ですが、ここではより汎用性を持たせるためにダウンロードファイルをbase64にエンコードして、base64の状態でダウンロードリンクを作成しています。
function createDataUrl(type) {
const mimeTypes = { csv: MimeType.MICROSOFT_EXCEL, pdf: MimeType.PDF };
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getActiveSheet();
let url = null;
if (type == "csv") {
url = `https://docs.google.com/spreadsheets/d/${ss.getId()}/gviz/tq?tqx=out:csv&gid=${sheet.getSheetId()}`;
} else if (type == "pdf") {
url = `https://docs.google.com/spreadsheets/d/${ss.getId()}/export?format=pdf&gid=${sheet.getSheetId()}`;
}
if (url) {
const blob = UrlFetchApp.fetch(url, {
headers: { authorization: `Bearer ${ScriptApp.getOAuthToken()}` },
}).getBlob();
return {
data:
`data:${mimeTypes[type]};base64,` +
Utilities.base64Encode(blob.getBytes()),
filename: `${sheet.getSheetName()}.${type}`,
};
}
return { data: null, filename: null };
}
base64への変換処理
ファイルをbase64に変換する処理は以下になります。
const blob = UrlFetchApp.fetch(url, {
headers: { authorization: `Bearer ${ScriptApp.getOAuthToken()}` },
}).getBlob();
return {
data:
`data:${mimeTypes[type]};base64,` +
Utilities.base64Encode(blob.getBytes()),
filename: `${sheet.getSheetName()}.${type}`,
};
Blobをbase64にエンコードする方法(解説用)
上記の処理はURLを利用してBlobファイルを取得しているため複雑に見えますが、より簡略化すると以下のようになります。
const blob = Utilities.newBlob(データ);
Utilities.base64Encode(blob.getBytes());
GASのUtilitiesクラスのnewBlobメソッドを使うことで、引数に指定したデータをBlobとして取得することができます。
このBlobデータに対してgetBytesメソッドを使うと、バイトとして取得することができます。
これを、Utilitesクラスのbase64Encodeメソッドを使ってbase64にエンコードします。
ダウンロードURLから取得したファイルをBlobファイルに変換する
今回の場合はGoogleドライブなどに保存されたファイルを直接blobファイルとして取得するのではなく、ダウンロードURLから取得したファイルをBlobファイルに変換します。
const blob = UrlFetchApp.fetch(url, {
headers: { authorization: `Bearer ${ScriptApp.getOAuthToken()}` },
}).getBlob();
UrlFetchApp
UrlFetchAppクラスを使うと、インターネット経由でリソースを取得することができます。他のWEBサイト上のデータを取得するなどスクレイピングで使われることが多いです。
fetch
fetchメソッドはUrlFetchAppクラスで使えるメソッドです。
fetch(url, パラメーター)
第1引数でアクセスしたいURLを指定し、第2引数に付与したいパラメータを指定します。第2引数は省略可能です。
パラメータは以下のようにオブジェクト形式で記述します。
{
'method' : 'post',
'contentType': 'application/json',
'payload' : JSON.stringify(data)
}
今回の場合は以下のように headersを指定しています。
UrlFetchApp.fetch(url, {headers: { authorization: `Bearer ${ScriptApp.getOAuthToken()}` }})
「Authorization」を使って認証処理をおこないたいのでヘッダーリクエストに認証情報を渡しています。
なお、headersなどのパラメータはJavaScriptのfetchメソッドに準拠しています。
(参考)
OAuth 2.0のアクセス トークンを取得
headers: { authorization: `Bearer ${ScriptApp.getOAuthToken()}` }
パラメーターとして「headers」にOAuth2.0認証用のアクセストークンを渡しています。
「Bearer(ベアラー)」とは認証方法の1つでBearer認証と呼ばれ、OAuth 2.0の仕様の一部として定義されています。
ScriptApp.getOAuthToken()
で、ScriptAppクラスのgetOAuthTokenメソッドを実行しOAuth2.0認証用のアクセストークンを取得しています。
(参考)getOAuthToken()
ここまでの処理でようやくダウンロードリンクを叩いて、ファイルにアクセスすることができます。
blobファイルとして取得する|getBlob
const blob = UrlFetchApp.fetch(url, {
headers: { authorization: `Bearer ${ScriptApp.getOAuthToken()}` },
}).getBlob();
最後にダウンロードリンクを叩いて得られたファイルをgetblobメソッドを使ってBlobとして取得しています。
base64のダウンロードリンクの作成
data:
`data:${mimeTypes[type]};base64,` +
Utilities.base64Encode(blob.getBytes()),
base64へのエンコード
Utilities.base64Encode(blob.getBytes())
Blobオブジェクトに変換したファイルをUtilitiesクラスのbase64encodeメソッドを使ってbase64にエンコードします。
base64にエンコードする際はgetBytesメソッドを使ってBlobオブジェクトをバイトデータにエンコードする必要があります。
base64ダウンロードリンクの作成
data:${mimeTypes[type]};base64,` + Utilities.base64Encode(blob.getBytes()),
base64ダウンロードURLの作成は以下のような構造になります。
data:[<mediatype>][;base64],<data>
冒頭を「data:」としてMIMEタイプ、「;bae64」、カンマで区切ってbase64形式のデータを記述します。
MIMEタイプは今回はCSVファイルの場合はExcel「MICROSOFT_EXCEL」、PDFの場合は「PDF」を指定しています。
なお、ダウンロードURLのdata:はデータURLと呼ばれ、小さなファイルをインラインで埋め込むためのスキームです。
(参考)Mmdn: データURL
以上でbase64形式のダウンロードURLの作成が完了です。
ファイル名の指定
createDataUrl関数の最後の処理としてファイル名を指定します。
filename: `${sheet.getSheetName()}.${type}`
やっていることはとても簡単です。sheet.getSheetName()
で現在のシートの名前を取得しています。
そこに、引数で渡された値を拡張子としてつけています。
ここの.${type}
がポイントです。ダウンロードURLはCSVファイルですが、TXTボタンをクリックすると変数typeに「txt」が渡されるので、ファイル名の拡張子が「.txt」となります。
これで、CSVファイルをカンマ区切りのテキストファイルとして命名することができます。
ボタンクリックでファイルをダウンロードする処理
AppScriptを使って、ファイルダウンロード用のURLとファイル名の生成ができたので、最後にボタンクリックでファイルをダウンロードする処理を記述します。
これが、index.htmlの中のwithSuccessHandler
メソッドで指定した処理になります。
google.script.run
.withSuccessHandler(({ data, filename }) => {
if (data && filename) {
const a = document.createElement("a");
document.body.appendChild(a);
a.download = filename;
a.href = data;
a.click();
}
})
document.createElement
document.createElementはタグを生成するJavaScriptのメソッドです。
document.createElement(タグ名[, オプション]);
第1引数でタグ名を文字列で指定することで、指定したタグを生成します。なお、ここで生成されたものをエレメントと呼びます。
document.createElement("a");
↓ aタグを生成します。
<a></a>
(参考)Mmdn: Document.createElement()
appendChild
親ノード.appendChild(エレメント)
appendChildは指定した親ノードの中に、指定したエレメントを挿入するメソッドです。
以下のようにすることで、bodyタグの中に先ほど作成したエレメント(aタグ)を挿入することができます。
document.body.appendChild(a)
↓
<body>
<a></a>
</body>
(参考)
エレメント.属性名=値
エレメント.属性名=値
エレメントに対して「.属性名=値」とすると、指定した属性と値を追加することができます。
aタグのhref属性
aタグにhref属性を付与すると、指定したURLがリンク先となります。
const a = document.createElement("a");
document.body.appendChild(a);
a.href = data;
↓
<body>
<a href="data:MIMEtype;base64,dabafgrag~"></a>
</body>
aタグのdownload属性
aタグにdownload属性を付与すると、hrefで指定したダウンロードURLをクリックしたときに、download属性の値がファイル名となります。
以下の場合aタグのエレメントにdownload属性を付与し、値は変数filenameになります。
a.download = filename;
↓
<body>
<a download="シート名.csv" href="data:MIMEtype;base64,dabafgrag~"></a>
</body>
これでダウンロード用のaタグが完成します。
clickメソッド
最後に、clickメソッドを実行して、aタグをクリックしています。
a.click();
これで、指定したダウンロードURLをクリックして、ファイルを指定したファイル名でダウンロードすることができます。
(参考)Mmdn: click()
以上で完成です。
お疲れさまでした^^