前回の続きです
blog.beatdjam.com
今回はDrive上のファイルの共有リンクを取得し、HTMLで作ったDL用のダイアログを表示させます。
また、スプレッドシートのメニューに任意のメニューを追加する方法も合わせて書きます。
ファイルの共有リンクを取得する
ファイルオブジェクトを取得する
共有リンクを取得するのはFileオブジェクトのIDを知る必要があります。
いくつか方法がありますが、今回はシンプルにDriveAppを用います。
フォルダ名(1階層)・ファイル名を指定して取得する場合はこのように書きます。
function getFileId(folderName, fileName) {
const folder = DriveApp.getFoldersByName(folderName).next();
return folder.getFilesByName(fileName).next();
}
もしDrive直下のファイルであれば、直接DriveAppから取得できます。
DriveApp.getFilesByName().next();
共有リンクを取得する
Fileには getDownloadUrl() が生えているので、これで共有リンクが取得できます。
function getDownloadUrl(folderName, fileName) {
const folder = DriveApp.getFoldersByName(folderName).next();
return folder.getFilesByName(fileName)
.next()
.getDownloadUrl();
}
アクセストークンをつける(任意)
共有リンクにはアクセストークンを付与することができます。
組織内で認証が必要な権限のファイルなどをGASで取得するような場合に、アクセストークンがついていないと認証エラーで上手く取得できません。
アクセストークンは ScriptApp.getOAuthToken()
で取得することができます。
これを前述したFile.getDownloadUrl()に下記のようにくっつけてやることで、認証可能なURLを生成できます。
file.getDownloadUrl() + "&access_token=" + ScriptApp.getOAuthToken()
HTMLテンプレートを利用してDL用のダイアログを作る
メニューに処理起動メニューを追加する
ここからHTMLをダイアログで表示する機能を作成しますが、その前にスプレッドシートからGASを起動するメニューを作成する必要があります。
というのも、UIに関わる操作はGASのエディタ上からは起動できず、スプレッドシートから起動しないと試せないからです。
手順は簡単で、onOpenハンドラ
にメソッドを呼び出す挙動を記述してやるだけです。
今回はダイアログ作成用の関数を呼び出しています。
function onOpen() {
const menu = SpreadsheetApp.getUi().createMenu('File Download');
menu.addItem('Exec', 'showDownloadModal');
menu.addToUi();
}
function showDownloadModal() {
const url = doCreateZip();
}
この処理を記述した状態でスプレッドシートをリロードすると登録したメニューが現れます。

この記事では扱いませんが、より柔軟なメニューの追加はこちらの記事が参考になります。
Google Apps Scriptを使った独自メニューの作り方 - Qiita
これで、ダイアログを開発するための準備が整いました。
Templated htmlについて
GASでは、HTMLファイルのテンプレートを読み込んで、動的なページを生成することができます。
HTML Service: Templated HTML | Apps Script | Google Developers
ざっくりいうと、下記のような構文が利用できます。
<?= ?> : テキストとして出力される。HTMLタグなどが含まれていた場合はエスケープされる。
<?!= ?> : テキストとして出力される。HTMLタグが含まれていてもそのまま埋め込まれる。
<? ?> : タグ内の処理がスクリプトとして実行される。実行結果は出力されない。
これを利用するとこんな表示の制御ができます。
function displaySample() {
const html = HtmlService.createTemplateFromFile("dialog.html");
html.isVisible = true;
SpreadsheetApp.getUi().showModalDialog(html.evaluate(), "Dialog");
}
<html>
<body>
<? if (isVisible) {?>
isVisibleがTrueのときのみこの要素が出力されます。
<? }?>
</body>
</html>
function displaySample() {
const html = HtmlService.createTemplateFromFile("dialog.html");
html.url = "https://example.com/";
SpreadsheetApp.getUi().showModalDialog(html.evaluate(), "Dialog");
}
<html>
<body>
<a href="<?=url?>">Link</a>
</body>
</html>
ダイアログの表示
先述したリンク生成の内容ほぼそのままですが、これでテンプレートから生成したHTMLをモーダルダイアログとして表示できます。
function showDownloadModal() {
const html = HtmlService.createTemplateFromFile("dialog.html");
html.url = doCreateZip();
SpreadsheetApp.getUi().showModalDialog(html.evaluate(), "Download");
}
<html>
<body>
<a href="<?=url?>">Link</a>
</body>
</html>

これで完成です!
おわりに(コード全文)
前回の記事からだいぶ間があいてしまいましたし、GASもリファクタの余地がある状態ですが、ひとまずまとめることができました。
あまりGASを触ったことがないけれど、似たような事がしたい方などに本記事が役立てば良いなと思います。
前回分も含めて記事内で登場したコードの全文を掲載して終わりたいと思います。ありがとうございました!
function onOpen() {
const menu = SpreadsheetApp.getUi().createMenu('File Download');
menu.addItem('Exec', 'showDownloadModal');
menu.addToUi();
}
function showDownloadModal() {
const html = HtmlService.createTemplateFromFile("dialog.html");
html.url = doCreateZip();
SpreadsheetApp.getUi().showModalDialog(html.evaluate(), "Download");
}
function doCreateZip() {
const sheetName = 'sample';
const jsonKey = 'sample_json';
const json = JSON.stringify({ jsonKey: getData(sheetName) });
const blobs = new Array();
blobs.push(Utilities.newBlob(json, 'application/json', 'hoge/fuga.json'));
const fileName = 'archive.zip';
const zip = Utilities.zip(blobs, fileName);
const folderName = 'temporary';
const folder = getOrCreateTempFolder(folderName);
const file = createFile(folder, zip);
return getDownloadUrl("temporary", "archive.zip");
}
function getData(sheetName) {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
const rows = sheet.getDataRange().getValues();
const keys = rows.splice(0, 1)[0];
return rows.map(row => {
const obj = {}
row.forEach((item, index) => { obj[keys[index]] = item; });
return obj;
});
}
function getOrCreateTempFolder(folderName) {
const folders = DriveApp.getFoldersByName(folderName);
if (folders.hasNext()) return folders.next()
else return DriveApp.createFolder(folderName);
}
function createFile(folder, file) {
const files = folder.getFilesByName(file.getName());
if (files.hasNext()) folder.removeFile(files.next());
return folder.createFile(file);
}
function getDownloadUrl(folderName, fileName) {
const folder = DriveApp.getFoldersByName(folderName).next();
return folder
.getFilesByName(fileName)
.next()
.getDownloadUrl();
}