【GAS】ボタンクリックでCSVやPDFの自動ダウンロードとして機能する関数の作成方法(AppScript)

Apps Script apps script GAS-prograshi(プロぐらし)-kv SpreadSheet
記事内に広告が含まれていることがあります。

Google App Script(GAS)を使うと、スプレッドシートなどのGoogleのサービスでダウンロードボタンを設置して、CSVファイルやPDFファイルをダウンロードすることができます。

ここでは、サイドバー形式でCSVとPDFのダウンロードボタンを設置してダウンロードする方法について使用しているコードの意味を含めて解説しています。


どんなことができるか?

ここで紹介するのは以下のようなプログラムです。

スプレッドシートの上部メニューに「プログラム」という項目を追加し、その中に「ファイルダウンロード」というメニューを設置します。


「ファイルダウンロード」をクリックするとサイドバーが開き、ダウンロードボタンが表示されます。


「CSV」または「PDF」をクリックすると、現在のスプレッドシートの内容をCSVやPDFファイルとして自動ダウンロードし、ダウンロードフォルダに格納するプログラムです。


ファイルを開くと以下のようになります。

▼CSVファイル


▼PDFファイル


コードの全体像

作成するのは以下の2つのファイルです。

作成するファイル
  1. メニューバー.gs
  2. index.html



それぞれのファイルのコードは以下になります。

function onOpen() {
  const ui = SpreadsheetApp.getUi();
  const menu = ui.createMenu('プログラム');
  menu.addItem('ファイルダウンロード', 'sidebarDownload');
  menu.addToUi();
}


function sidebarDownload() {
  const html = HtmlService.createHtmlOutputFromFile("index").setTitle("ダウンロードボタン");
  SpreadsheetApp.getUi().showSidebar(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") {
    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 };
}


<!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')" /><br>
      <input type="button" value="PDF" onclick="download('pdf')" />
    </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>



サイドバーの作成方法

スプレッドシートにサイドバーを表示する方法はここでは割愛します。詳細は下記をご参考ください。


作成したときに、AppScriptとHTMLファイルの中身をそれぞれ上記のファイルにしてください。


ダウンロード処理の流れ

サイドバーでHTMLファイルを表示する

続いて実際のダウンロード処理の流れについて解説します。

スプレッドシート上で「ファイルダウンロード」というメニューをクリックすると「sidebarDownload」という関数を実行します。

function sidebarDownload() {
  const html = HtmlService.createHtmlOutputFromFile("index").setTitle("ダウンロードボタン");
  SpreadsheetApp.getUi().showSidebar(html);
}


これでサイドバーに指定したHTMLファイルの内容を表示します。


onclickイベント

ボタンをクリックするとHTMLファイルで割り当てられているonclickイベントが発動します。

  <input type="button" value="CSV" onclick="download('csv')" /><br>
  <input type="button" value="PDF" onclick="download('pdf')" />


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"、PDFボタンがクリックされた場合はtype="pdf"となります。

ここの処理は結構肝なので、下の方に切り出して詳細を解説します。


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") {
    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 };
}

この処理を実行することで、スプレッドシートをCSVやPDFに変換してダウンロードするためのリンクを生成します。

戻り値の部分は以下になります。

    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") {
    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 };
}



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とは、64進数でのデータ表記です。

すべてのデータをアルファベット(a~z, A~Z)と数字(0~9)、記号(+, ー)の64文字でエンコードします。

base64はすべてのデータを英数字で表すMIME(Multipurpose Internet Mail Extensions)という企画の中で定められたデータ変換方法です。

base64を使うことで特殊文字を英数字に変換したり、画像や音声データを英数字に変換することができます。

つまり、PDFファイルや画像、音声などをbase64でエンコードし英数字に変換し、メーラーやHTMLなどでそれらをそのまま通常のデータのように扱うことができます。



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にエンコードします。

Blobとは何か?

GASを使っているとBlobをよく使います。Blobとは何か?については下記をご参考ください。

【GAS】Blobとは何か?実例で解説|Utilities.newBlobやMIMEタイプの指定方法



ダウンロードURLから取得したファイルをBlobファイルに変換する

今回の場合はGoogleドライブなどに保存されたファイルを直接blobファイルとして取得するのではなく、ダウンロードURLから取得したファイルをBlobファイルに変換します。

    const blob = UrlFetchApp.fetch(url, {
      headers: { authorization: `Bearer ${ScriptApp.getOAuthToken()}` },
    }).getBlob();


UrlFetchApp

UrlFetchAppクラスを使うと、インターネット経由でリソースを取得することができます。他のWEBサイト上のデータを取得するなどスクレイピングで使われることが多いです。


(参考)Class UrlFetchApp


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」を指定しています。


MIMEタイプとは何か?

MIMEタイプはGASでも非常によく使うものです。詳細については下記をご参考ください。

【GAS】MIMEタイプとは何か?AppScriptで使えるmimeTYPEの一覧を実例で解説


なお、ダウンロードURLのdata:データURLと呼ばれ、小さなファイルをインラインで埋め込むためのスキームです。

(参考)Mmdn: データURL


以上でbase64形式のダウンロードURLの作成が完了です。


ファイル名の指定

createDataUrl関数の最後の処理としてファイル名を指定します。

filename: `${sheet.getSheetName()}.${type}`


やっていることはとても簡単です。sheet.getSheetName()で現在のシートの名前を取得しています。

そこに、引数で渡された値を拡張子としてつけています。



ボタンクリックでファイルをダウンロードする処理

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()


以上で完成です。

お疲れさまでした^^



タイトルとURLをコピーしました