Apps Script用Sheet生成動態網頁(20): 移植Google Drive檔案列表

第六篇的時候我們製作了瀏覽Drive上檔案的UI,因為我們改成用React範本來建構網站,所以些就來移植之前的成果,透過移植的過程,我們也可以更好的了解React與Apps Script之間的互動方法,之後幾篇都會持續移植之前的成果。

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

指定Drive資料夾並增加權限

因為要讀取Google Drive上的資料,所以需要增加權限

修改appsscript.json在oauthScopes陣列中加入drive的權限

"oauthScopes": [
"https://www.googleapis.com/auth/drive"
],

在src/server/settings.js中新增Drive資料夾的ID,例如網址為 https://drive.google.com/drive/folders/1YUL...0kOu?usp=sharing 時,程式碼加入

export const FOLDER_ID = '1YUL...0kOu';

加入新的API

在src/server/index.js裡頭,將讀取Drive資料的API  listFilesInDriveFolder()暴露給前端

import * as driveFunctions from './drive';
...
global.listFilesInDriveFolder = driveFunctions.listFilesInDriveFolder;

在src/server/drive.js實作功能,是移植第六篇的程式碼並調整如下:

import { FOLDER_ID } from './settings';

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

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

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


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

因為在第18篇製作選單時已經先加入DriveLister的route,所以我們不需要增加新的route,只要修改DriveLister內容即可。

因為檔案列表的設計就是用一個json,迴圈的跑過去,所以我們就可以先建構單一檔案的呈現元件ShowOneFile,再用迴圈將它呈現出來。

所以src/client/demo-bootstrap/pages/DirveLister.jsx修改如下

import React, { useEffect, useState } from 'react';
import { Container, Row } from 'react-bootstrap';
import PropTypes from 'prop-types';
import Server from '../../utils/server';

const { serverFunctions } = Server;

const ShowOneFile = ({ file }) => {
return (
<div className="col-12 col-sm-6 col-md-4">
<div className="card p-5">
<img
className="card-img-top rounded bg-secondary bg-gradient"
data-thumb={file.thumbnail}
/>
<div className="card-body">
<p className="card-text">
檔案名稱:{file.name}
<br />
檔案大小:{file.size}位元組
<br />
</p>
<a className="btn btn-primary" href={file.url}>
點此下載
</a>
</div>
</div>
</div>
);
};

ShowOneFile.propTypes = {
file: PropTypes.object,
};

const DirveLister = () => {
const [fileList, setFileList] = useState([]);

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

useEffect(() => {
serverFunctions
.listFilesInDriveFolder()
.then(setFileList)
.catch(alert)
.then(showDataThumbnails);
}, []);

return (
<Container>
<h1>Google Drive檔案列表</h1>
<Row>
{fileList.map((file, idx) =>
<ShowOneFile key={idx} file={file} />
)}
</Row>
</Container>
);
};

export default DirveLister;

這裡可以看到分成兩個部份:ShowOneFile和DirveLister。

  • ShowOneFile就是呈現單一個檔案的資料,包含檔案大小、預覽圖、下載按鈕等功能。
  • DirveLister則是與後端API溝通,取得檔案列表的資料。

我們透過React範本所指導的方式,引用server的API,serverFunctions就會包含我們上面新增在後端的API,listFilesInDriveFolder()。

在useEffect裡頭,我們設定了會從Server端取回Drive檔案列表,再透過setFileList設定到state中,讓UI界面去呈現整個列表。最後再呼叫showDataThumbnails()將img的縮圖的BASE64資料變成實際呈現的圖片。

import Server from '../../utils/server';

const { serverFunctions } = Server;
...
useEffect(() => {
serverFunctions
.listFilesInDriveFolder()
.then(setFileList)
.catch(alert)
.then(showDataThumbnails);
}, []);



在useEffect的第二個參數, [],是表示這個函數在元件呈現時會做一次裡頭所寫的事情,也就是載入DirveLister這個頁面的時候做。

呈現列表就只需要用了一個.map來迴圈跑過去,剩下就交給ShowOneFile來處理。
return (
<Container>
<h1>Google Drive檔案列表</h1>
<Row>
{fileList.map((file, idx) =>
<ShowOneFile key={idx} file={file} />
)}
</Row>
</Container>
);

3. 展示



留言