GASでWebアプリを作ってローカルファイルをGoogleドライブにアップしたい
以下の条件を満たすもの
条件
- アップロード先はGoogleドライブの共有フォルダ
- そのフォルダには権限が設定されている
- Webアプリを開いたユーザーはそのフォルダにアクセスする権限がない
- アップロード後、3のユーザにアップロードしたファイルだけ閲覧権限を付与する
というニッチな条件を満たすために考えました。
条件3をどうするか
「Webアプリを開いたユーザーはそのフォルダにアクセスする権限がない」
権限がないので、ファイルをアップロードする直前に権限を付与する?
- 閲覧権限だけだとアップロードできないので編集権限も必要
- フォルダへの編集権限を付与すると、フォルダ内にある他のファイルも閲覧、編集できる
- そうなると4を満たせない
そこで考えた方法
「フォルダの編集権限を持ったユーザーが代わりにファイルをアップロードする」
Webアプリを開いたユーザーとは別のAdminユーザにやってもらう。
もう一つGASのプロジェクトを作るとできそう。
どうやるか考えてみる。
ファイルをアップロードする処理は別のアプリを作って実行するそれをライブラリとして扱うライブラリ側アプリの実行ユーザーはアップロード先フォルダの編集権限を持っている
ライブラリでは実行ユーザーを指定できないため、doPostで考えてみる。(2022/01/03 追記)
- ファイルアップロードするdoPostの処理を別のプロジェクトで作る
- doPost側でpayloadを受け取ってドライブにアップロードする
- doPost側のアプリの実行者は自分(アップロード先フォルダの編集権限を持っている)
これでできそう。
そして書いたコード
画面を操作する側のGASプロジェクト
コード.gs
function doGet() {
return HtmlService.createHtmlOutputFromFile("index");
}
function upload_file_gs(reader_result, file_name) {
var email = Session.getActiveUser().getEmail();// ファイルの閲覧権限を追加したいユーザー(この例では実行したユーザーを指定)
var payload = {
"reader_result": reader_result,
"file_name": file_name,
"email": email
}
const options = {
"method": "post",
"payload": JSON.stringify(payload),
"contentType": "application/json"
}
var url = "doPost側のWebアプリのURL";
const response = UrlFetchApp.fetch(url, options).getContentText();
return response;
}ウェブアプリケーションのデプロイ設定
※グレーのコードはライブラリで考えたときのコードの備忘録(不完全)
コード.gs
function doGet() {
return HtmlService.createHtmlOutputFromFile("index");
}
function upload_file_gs(reader_result, file_name) {
var email = "追加したいメールアドレス";
var file_url = FileUpload.run(reader_result, file_name, email)
return file_url;
}
|
index.html
<!DOCTYPE html>
<html>
<body>
<form>
<div>
<input type="file" id="my_files" multiple>
</div>
</form>
<label id="upload_label"></label>
<div id="upload_div"></div>
<script>
document.getElementById("my_files").addEventListener("change", upload_files);
var files_len = 0;
var uploaded_len = 0;
function upload_files() {
var files = document.getElementById("my_files").files;
files_len = files.length;
for (var i = 0; i < files.length; i++) {
uploadFile(files[i]);
document.getElementById("upload_label").textContent = "アップロードしています...";
}
}
function uploadFile(file) {
var reader = new FileReader();
reader.onload = function() {
var reader_result = reader.result;
var file_name = file.name
google.script.run
.withSuccessHandler(uploaded)
.upload_file_gs(reader_result, file_name);
}
reader.readAsDataURL(file);
}
function uploaded(file_url) {
create_link(file_url);
uploaded_len++;
if (uploaded_len == files_len) {
document.getElementById("upload_label").textContent = "アップロードしました";
}
}
function create_link(file_url) {
var div = document.getElementById("upload_div");
var br = document.createElement("br");
var link = document.createElement("a");
link.textContent = file_url;
link.setAttribute("href", file_url);
link.setAttribute("target", "_blank");
div.appendChild(link);
div.appendChild(br);
}
</script>
</body>
</html>
|
doPost側のGASプロジェクト
コード.gs
/************************************
payloadはeventObject.postData.contentsで取得できる
取得してGoogleドライブに保存する
************************************/
function doPost(eventObject) {
const contents = JSON.parse(eventObject.postData.contents);
const created = createFile(contents);
return ContentService.createTextOutput(created).setMimeType(ContentService.MimeType.JSON);
}
const FOLDER_ID = "GoogleドライブのフォルダID";
function createFile(contents) {
var reader_result = contents["reader_result"];
var file_name = contents["file_name"];
var email = contents["email"];
var result_split = reader_result.split(',');
var content_type = result_split[0].split(';')[0].replace('data:', '');
var row_data = result_split[1];
var data = Utilities.base64Decode(row_data);
var file = Utilities.newBlob(data, content_type, file_name);
var folder = DriveApp.getFolderById(FOLDER_ID);
var drive_file = folder.createFile(file);
var file_url = drive_file.getUrl();
drive_file.addViewer(email);
return file_url;
}
ウェブアプリケーションのデプロイ設定
※グレーのコードはライブラリで考えたときのコードの備忘録(不完全)
コード.gsvar FOLDER_ID = "保存先フォルダのID";
function run(reader_result, file_name, email) {
var result_split = reader_result.split(',');
var content_type = result_split[0].split(';')[0].replace('data:', '');
var row_data = result_split[1];
var data = Utilities.base64Decode(row_data);
var file = Utilities.newBlob(data, content_type, file_name);
var folder = DriveApp.getFolderById(FOLDER_ID);
var drive_file = folder.createFile(file);
var file_url = drive_file.getUrl();
drive_file.addViewer(email);
return file_url;
}
|
実行手順
操作する側のWebアプリを開いて、以下のようにファイルをアップロードします。
「ファイル選択」をクリックして出てくるモーダルでローカルファイルを開きます。
「アップロードしています...」表示になります。
アップロードされたファイルへのリンクが表示されます。
関連記事
index.htmlはこれと同じです。
Google Driveに複数ファイルをアップロードしたい(submitボタン使わない)
GASでAPIを作ってPOSTでpayloadを渡したい
GASのライブラリの扱いは別途書き残しました。
Google Apps Scriptでライブラリを作って使う