Google Apps ScriptでCloud Functionsの関数を実行する【GAS】
前回までで、Google Cloud Functionsを利用して、すでにCloud Storageに配置してあるPDFについて、パスワード付PDFにして、再度Cloud Storageに格納するところまで成功しました。これで、Cloud Functions側にGASからの受け入れ窓口と、取得したデータをバイナリで返してくれさえすれば、Google Apps Script + Google Cloud Functionsの合わせ技で、パスワードPDFを一気につくれます。
ようやくこれで、5年越しに低価格で高速なパスワードPDFをGASから作る環境が作れました。Cloud FunctionsはGAS単体やGCP単体では難しい今回のようなケースで、Node.jsとモジュールの力を借りて、機能拡張するには非常に良い選択肢であると、検証できたと思います。
※これにてパスワードPDFは作れるようになりましたが、同じ要領で暗号化ZIPも作れるかもしれません。また、複数のユーザで使う場合には、ファイル名+シート名+日付といったようなファイル名を付けて処理をすると良いでしょう。
※また、この関数実行URLは知っていれば誰でも実行できてしまうので、課金でCloud死してしまいます。独自にAPIキー的なものを実装して、APIキーが無いアクセスは弾くようにしてしまえば良いでしょう。
目次
今回使用するスプレッドシート
今回は単純に1枚だけ、PDF化するためだけのシートを加えています。スクリプトエディタの中にて、run_gcffunction関数を実行すると、GCFにパラメータを送り、パスワードPDFをバイナリで取得して、ドライブに生成します。もちろん、そのままメールに添付して送る事も可能です。
事前準備
Cloud Storage
Cloud Functions
{
"name": "sample-http",
"version": "0.0.1",
"dependencies": {
"hummus-recipe": "^1.8.9",
"@google-cloud/storage": "^2.5.0",
"request": "^2.88.0"
}
}
コード:package.jsonには以上のような記述を追加
ソースコード
GAS側コード
//cloud functionsの関数のURL
var url = "ここにGoogle Cloud Functionsの実行用URLを入力する";
//スプレッドシートURLを入力
var ssid = "PDF化するスプレッドシートのIDを入力する";
//PDF生成先フォルダーのID
var folder = "ここにパスワードPDFを生成するGoogle DriveのフォルダのIDを入力する";
//Cloud Functionsの関数を実行する
function run_gcffunction() {
//uiを取得
var ui = SpreadsheetApp.getUi();
//Access Tokenを取得
var accessToken = ScriptApp.getOAuthToken();
//スプレッドシート情報を取得する
var sheetname = "ここにPDF化するシート名を入力する";
var sheetid = SpreadsheetApp.openById(ssid).getSheetByName(sheetname).getSheetId();
//送信パラメータを組み立てる
var payload = {
"key": ssid,
"sheetid" : sheetid,
"sheetname" : sheetname,
"accesstoken": accessToken,
"passwd": "tomatoman", //PDFのパスワードを設定します。
};
//POSTで関数を実行する
var response = UrlFetchApp.fetch(url, {
method: 'POST',
contentType: "application/json",
payload : JSON.stringify(payload),
muteHttpExceptions: true
});
//サーバーレスポンスコードを取得する
var resCode = response.getResponseCode();
//リターンされて来たバイナリデータをドライブに作成する
if (resCode === 200) {
//バイナリデータを取得する(ファイル名も付けておく)
var binary = response.getBlob().setName(sheetname + ".pdf");
//Google DriveにパスワードPDFを生成する
DriveApp.getFolderById(folder).createFile(binary);
//終了メッセージ
ui.alert("パスワードPDFファイルが生成されましたよ!!");
}else{
//エラーメッセージ
ui.alert(resCode + "エラーが発生しました。");
}
}
- GCF側の関数実行用URL、スプレッドシートのID、ドライブのフォルダのIDなどを変数として入力しておきます。
- GAS側でPDFは生成させないので、今回はGCF側関数にJSON形式でパラメータを渡してあげます。
- その際に、Access Tokenも必要なので、ScriptApp.getOAuthToken()にて取得しておきましょう。
- 必ず、POSTで送信します。UrlfetchApp1回だけでパス付PDFが作れるので、時間コストはこれまでのPDF生成と同じです。(GCFのお金は掛かるけれどね)
- サーバレスポンスコードが200ならば、GCF側からパス付PDFのバイナリデータを受け取り、DriveAppのcreateFileメソッドにて、ファイルを生成します。
今回の開発で300回くらい実行していますが、果たしてコストどれくらいかかったのかな。多分GCF無料枠とGCPの無料枠で済んでいるはず!!
GCF側コード
//標準モジュールを読み込み
var fs = require('fs');
//追加モジュールの読み込み
const {Storage} = require('@google-cloud/storage');
const request = require('request');
const HummusRecipe = require('hummus-recipe');
//バケットとtmpフォルダの指定
const gcs = new Storage();
const bucket = gcs.bucket('drive2gcs');
const temp = "/tmp/"
exports.sspdfget = (req, res) => {
//JSONリクエストパラメータを取得する
const json =req.body;
//個別パラメータを取得する
var key = json.key;
var sheetid = json.sheetid;
var sheetname = json.sheetname;
var token = json.accesstoken;
var pw = json.passwd;
//PDF化する為のURLを組み立てる
var url = "https://docs.google.com/spreadsheets/d/" + key + "/export?gid=" + sheetid + "&format=pdf&portrait=false&size=A4&gridlines=false&fitw=true"
//リクエストオプション
var options = {
url: url,
method: 'POST',
headers: {
'Authorization': 'Bearer ' + token,
"Content-type": "applcation/pdf"
},
encoding: "binary"
};
//requestにてスプレッドシートをPDF化して受け取る
request(options, function(error, response, body) {
if (!error && response.statusCode == 200) {
new Promise(function(resolve, reject) {
//ファイルを一時フォルダに保存
fs.writeFileSync(temp + sheetname + ".pdf", body, 'binary');
resolve("OK");
}).then((result) => {
//ダウンロード完了
console.log(result);
//パスワード化を実施する
passwordpdf([sheetname,pw],function (ret){
//パスワード化完了
console.log(ret);
//GCSへアップロードする
var options = {
destination: "/public/" + sheetname + "_encrypt.pdf"
};
bucket.upload(temp + sheetname + "_encrypt.pdf", options, function(err, file) {
console.log("File Uploaded");
}).then(()=>{
return;
});
});
}).then((result) => {
//ファイルサイズを取得する
var stat = fs.statSync(temp + sheetname + "_encrypt.pdf");
console.log(stat.size + "バイト");
//PDFファイルを取得する
var pdf = fs.readFileSync(temp + sheetname + "_encrypt.pdf");
//バイナリデータを返却する
res.writeHead(200, {"Content-Type": "application/pdf"});
res.write(pdf, "binary");
res.end();
});
} else {
// エラーハンドラー
res.status(503).send("エラー");
}
});
};
//PDFにパスワード化を実施する
function passwordpdf(args,callback){
//引数を取得する
var sheetname = args[0];
var pass = args[1];
const pdfDoc = new HummusRecipe(temp + sheetname + ".pdf", temp + sheetname + "_encrypt.pdf");
pdfDoc.encrypt({
userPassword: pass,
ownerPassword: pass,
userProtectionFlag: 4
}).endPDF(function(ret){
//5秒間スリープを入れる
sleep(5000, function() {
callback("Encryption End");
});
});
}
//sleep関数
function sleep(waitSec, callback) {
setTimeout(callback, waitSec);
}
実行してみる
図:encryptなPDFが生成されました。
図:Google Drive側にも生成されました。
関連リンク
- [NodeJS] ファイルをダウンロードする
- Express - Return binary data from distant webservice
- Google Cloud Functions で Post の引数を受け取る
- Cloud Functions with Puppeteer + Google Apps Script でスクレイピングサーバーをサクッと作る
- Binary response via Google Cloud Functions
- cloud functions × puppeteer × Google Apps Scriptで超低コスト定期実行クローラを作って金曜ロードショーを毎週slackに通知させる1
- Qiita Contributions API を Google Cloud Functions に作ってみた
- request-promiseを使ったHTTPクライアントを作る
- 【Node.js入門】requestモジュールでGET / POST通信する方法!
- LINE BOTからNode.jsで画像を受け取って保存する
- [Google Cloud Functions]でQRコード作成APIを構築!
- Generate a Pdf with Google Cloud Functions
- [Node.js]Basse64エンコード、Base64デコードしてみた
- Binary response via Google Cloud Functions
- 拡張子とMIMEタイプ
- ReadFile in Base64 Nodejs
- Encoding PDF binary data to base64 not working with NodeJS
- Google App Script の UrlFetchApp の 例外ハンドリングについて
- Firebase Hosting と Cloud Function を組み合わせた時APIが外から呼ばれ放題な問題とかどうしようかなと思った話
- Firebase Functionsのhttpsトリガーの関数を特定の人からしかできないように制限したい
- Cloud FunctionsでIAMを利用する
- Google Cloud Functionsの関数コール権をIAMで管理するためのテクニック
- HTTP Cloud Functions for Firebase で HTTP リクエストの発信元を認証する方法
- [Docs] TypeError: Cannot read property 'OAuth2' of undefined
- HTTP Cloud Function での認証




package.jsonの記述ですが、「”request”: “^2.88.0”,」の最後のカンマは不要でした。
これのエラー内容が非常に分かりづらかったので、このように記載を直してほしいです。
——–
{
“name”: “sample-http”,
“version”: “0.0.1”,
“dependencies”: {
“hummus-recipe”: “^1.8.9”,
“@google-cloud/storage”: “^2.5.0”,
“request”: “^2.88.0”
}
}
——–
失礼しました。直しました。
カンマ削りを忘れておりました。