Apps Script用Sheet生成動態網頁(6): 製作瀏覽上傳檔案的UI

在前一篇Apps Script用Sheet生成動態網頁(5): 上傳檔案中,我們完成了上傳任意類型的檔案,不過我們不想整個share資料給所有,想從我們的AppsScript網頁呈現,所以就需要製作瀏覽上傳檔案的UI。

要製作一個完備的瀏覽UI需要比較多的前端知識,不過如果是要把檔案的資訊顯示出來,只需要找一套好懂的UI框架就好。這裡會使用Bootstrap 5.0作為範例,本篇不會著墨在UI的用法,若有不懂的部份可以在下面留言提問。我們這就開始吧!


1. Apps Script的部份(後端/伺服端)

本次會用到的後端部份有三個函數: listFilesInDriveFolder()、getFileInfo()以及encodeBlob(),入口點是listFilesInDriveFolder(),用來取得Google Drive資料夾下的所有檔案,再將我們需要單個檔案資訊交給getFileInfo()取出來,然後因為我想要取得檔案的縮圖所以又用encodeBlob()來將縮圖的Blob資料轉成DataURL的字串傳回給前端。

程式碼如下:

function encodeBlob(blob) {
if (!blob) return null;
let type = blob.getContentType();
let base64 = Utilities.base64Encode(blob.getBytes())
return `data:${type};base64,${base64}`;
}

function getFileInfo(file) {
return {
name: file.getName(),
thumbnail: encodeBlob(file.getThumbnail()),
size: file.getSize(),
url: file.getDownloadUrl(),
}
}

function listFilesInDriveFolder() {
let fileList = [];
let folder = DriveApp.getFolderById(FOLDER_ID);
let fileItr = folder.getFiles();
while (fileItr.hasNext()) {
let file = fileItr.next();
fileList.push(getFileInfo(file));
}
return fileList;
}


2. HTML部份(前端/使用者端)

前端部份部份使用Apps Script使用的模板,裡面引用了Bootstrap 5.1.3的CSS和Javascript。然後使用card來作為每個檔案的UI呈現方式。

const LIST_FILES_TEMPLATE_PAGE = `<!DOCTYPE html>
<html>
<head><base target="_top">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
</head>
<body onload="init()">
<div class="container">
<div class="row"><h1>上傳到Drive資料夾的檔案</h1></div>
<div class="row g-4">
<?
let list = listFilesInDriveFolder();
for (let i = 0 ; i < list.length ; ++i ) {
let file = list[i];
?>
<div class="col-12 col-sm-6 col-md-4">
<div class="card p-5">
<img class="card-img-top rounded bg-secondary bg-gradient" alt="<?= file.name ?>" data-thumb="<?= file.thumbnail ? file.thumbnail : '' ?>">
<div class="card-body">
<p class="card-text">
檔案名稱:<?= file.name ?><br/>
檔案大小:<?= file.size ?>位元組<br/>
</p>
<a href="<?= file.url ?>" class="btn btn-primary">點此下載</a>
</div>
</div>
</div>
<?
}
?>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
<script>
function init() {
console.log('init');
Array.from(document.getElementsByTagName('img')).forEach(e => {
if (e.hasAttribute('data-thumb')) e.src = e.getAttribute('data-thumb')
});
}
</script>
</body>
</html>`;

在這個UI網頁中最特別的部份是縮圖呈現的處理。我們從後端取得的DataURL字串會放在img元件的data-thumb屬性,然後在init()函數裡面將data-thumb指定給src屬性。

Array.from(document.getElementsByTagName('img')).forEach(e => {
    if (e.hasAttribute('data-thumb'))
        e.src = e.getAttribute('data-thumb')
});

這個部份主要是為了繞過appscript的奇怪限制,直接寫在src屬性裡面總是顯示不出縮圖,但是透過這種在client端處理的間接處理,竟然就正常呈現了。

為了呈現上面的瀏覽上傳檔案的UI,我們需要在doGet裡面加上新的分歧判斷
function doGet(req) {
...
else if ('filesInDrive' === req.parameter['show']) {
output = HtmlService.createTemplate(LIST_FILES_TEMPLATE_PAGE).evaluate();
}
...
}


為了方便,我們也在首頁的template字串裡面加上一行方便點擊。
<li><a href="<?= BASE_URL + '?show=filesInDrive' ?>">上傳到Drive資料夾的檔案</a></li>

3. 展示



留言