ð āđāļāļēāļ°āļĨึāļāļāļēāļĢāļāļģāļāļēāļāļāļāļ "āļĢāļ°āļāļāļัāļāđāļŦāļĨāļāļั้āļāļŠูāļ (Advanced Upload System)" āđāļāđāļāļĢāđāļāļāļ์ āļĢāļ°āļāļāļāļĨัāļāļŠื่āļāļāļēāļĢāđāļĢีāļĒāļāļĢู้ (Knowledge Hub) āļāļĢัāļ
āļĢāļ°āļāļāļี้āļูāļāļāļāļāđāļāļāļĄāļēāđāļื่āļāđāļ้āļัāļāļŦāļēāļāļāļāļ§āļ (Bottleneck) āļāļāļ Google Apps Script (GAS) āđāļāļĒāđāļāļāļēāļ° āļึ่āļāļāļāļิ GAS āļāļ°āļĢัāļāđāļāļĨ์āđāļ้āļāļāļēāļāļāļģāļัāļ (āđāļĄ่āđāļิāļ 50MB) āđāļĨāļ°āļĄัāļāļāļ°āđāļิāļ Time-out āļŦāļēāļāļัāļāđāļŦāļĨāļāļāļēāļ āđāļ่āļĢāļ°āļāļāļี้āđāļ้āļืāļāđāļāļāļิāļ "Direct Resumable Upload to Google Drive API" āļāļĢัāļ
ð§ą āļŦāļĨัāļāļāļēāļĢāļāļģāļāļēāļāļ āļēāļāļĢāļ§āļĄ (Concept)
āđāļāļāļี่āļāļ°āļŠ่āļāđāļāļĨ์āļāļēāļ Client (āļŦāļ้āļēāđāļ§็āļ) ➔ GAS Server ➔ Google Drive (āļึ่āļāļ้āļēāđāļĨāļ°āļิāļ Limit) āļĢāļ°āļāļāļี้āļāļ°āļŠ่āļāđāļāļĨ์āļāļēāļ Client (āļŦāļ้āļēāđāļ§็āļ) ➔ Google Drive API āđāļāļĒāļāļĢāļ
GAS Server āļāļ°āļāļģāļŦāļ้āļēāļี่āđāļ่ "āļู้āļāļĢāļ°āļŠāļēāļāļāļēāļ" (āđāļŦ้āļุāļāđāļāđāļ้āļēāļ้āļēāļ āđāļĨāļ° āļĢัāļāđāļĨāļāļี่āđāļāļāļŠāļēāļĢāđāļāļัāļāļึāļ) āđāļ่āđāļĄ่āđāļ้āđāļ็āļāļāļāļāļāļĒ้āļēāļĒāļāļāļāđāļāļ
ð ️ āđāļāļēāļ°āļĨึāļāđāļ้āļāļีāļĨāļ°āļŠ่āļ§āļ
1️⃣ āļั่āļ Client (script.html)
āļี่āļืāļāļŠ่āļ§āļāļี่āļāļģāļāļēāļāļŦāļัāļāļี่āļŠุāļ āđāļāļĒāļĄีāļัāļāļ์āļัāļāļŦัāļ§āđāļāļŦāļĨัāļ 2 āļัāļ§āļāļĢัāļ:
āļัāļāļ์āļัāļ A: performUploadsAndSubmit (āļู้āļัāļāļāļēāļĢāļāļēāļĢāļัāļāđāļŦāļĨāļ) āļāļģāļŦāļ้āļēāļี่āļāļ§āļāļุāļĄ Flow āļāļēāļĢāļāļģāļāļēāļāļั้āļāļŦāļĄāļ:
1. āļāļ Token: āđāļĢีāļĒāļ getAuthToken() āļāļēāļ Server āđāļื่āļāļāļāļุāļāđāļāļั่āļ§āļāļĢāļēāļ§ (OAuth Token) āļŠāļģāļŦāļĢัāļāđāļ้āļēāļึāļ Drive
2. āļ§āļāļĨูāļāļัāļāđāļŦāļĨāļ: āļāļģāđāļāļĨ์āđāļāļิāļ§āļĄāļēāļีāļĨāļ°āđāļāļĨ์ āđāļĨ้āļ§āļŠ่āļāđāļāđāļŦ้āļัāļāļ์āļัāļ uploadFileResumable
3. āļŠ่āļāļ้āļāļĄูāļĨāđāļ้āļē Sheet: āđāļĄื่āļāđāļ้ File ID āļāļĢāļāļุāļāđāļāļĨ์āđāļĨ้āļ§ āļ่āļāļĒāļŠ่āļāļ้āļāļĄูāļĨ (Metadata + File ID) āđāļāļัāļāļึāļāļĨāļ Sheet āļ่āļēāļ google.script.run.saveResource()
async function performUploadsAndSubmit(payload, fileList, submitFunc) {
// 1. āļāļāļุāļāđāļ (Token)
var token = await new Promise((resolve, reject) => {
google.script.run.withSuccessHandler(resolve).getAuthToken();
});
// 2. āļ§āļāļĨูāļāļัāļāđāļŦāļĨāļāļีāļĨāļ°āđāļāļĨ์
for (var i = 0; i < fileList.length; i++) {
// āđāļĢีāļĒāļāļัāļāļ์āļัāļ B āđāļื่āļāļĒิāļāđāļāļĨ์āđāļ้āļē Drive āđāļāļĒāļāļĢāļ
var fileId = await uploadFileResumable(fileList[i], token, (percent) => {
// āļัāļāđāļāļ Progress bar
showLoader(true, '... (' + percent + '%)');
});
// āđāļ็āļ File ID āđāļ§้āđāļ้āļāļēāļ
uploadedFiles.push({ id: fileId, ... });
}
// 3. āļŠ่āļāđāļ่ "File ID" āđāļāļัāļāļึāļ (āđāļĄ่āđāļ่āļัāļ§āđāļāļĨ์)
submitFunc(payload);
}
āļัāļāļ์āļัāļ B: uploadFileResumable (āļāļāļāļāļāļāļ) āļี่āļืāļāđāļāļāļิāļāļั้āļāļŠูāļ āđāļ้ fetch āđāļĨāļ° XMLHttpRequest āļĒิāļ API āļāļāļ Google Drive:
1. Init (POST): āļŠ่āļ metadata (āļื่āļāđāļāļĨ์, āļāļĢāļ°āđāļ āļ) āđāļāļāļāļ Google āļ§่āļē "āļāļģāļĨัāļāļāļ°āļŠ่āļāđāļāļĨ์āđāļāļāļ°"
2. Get Location: Google āļāļ°āļāļāļāļāļĨัāļāļĄāļēāļāļĢ้āļāļĄ Location URL (āļĨิ้āļāļ์āļŠāļģāļŦāļĢัāļāļัāļāđāļŦāļĨāļ)
3. Upload (PUT): āļŠ่āļāđāļื้āļāđāļāļĨ์ (Binary) āđāļāļี่ Location URL āļั้āļ āļāļĢ้āļāļĄāļัāļ§āļัāļ Progress
function uploadFileResumable(file, accessToken, onProgress) {
return new Promise((resolve, reject) => {
// ...āđāļāļĢีāļĒāļĄ Metadata...
// 1. āđāļĢิ่āļĄāļ้āļ Session (POST)
fetch('https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable', {
method: 'POST',
headers: { 'Authorization': 'Bearer ' + accessToken, ... },
body: JSON.stringify(metadata)
}).then(response => {
// 2. āļĢัāļ URL āļāļĨāļēāļĒāļāļēāļ
return response.headers.get('Location');
}).then(locationUrl => {
// 3. āļŠ่āļāđāļāļĨ์āļāļĢิāļ (PUT) āļ่āļēāļ XMLHttpRequest āđāļื่āļāļ§ัāļ % āđāļ้
var xhr = new XMLHttpRequest();
xhr.open('PUT', locationUrl, true);
// āļŠูāļāļĢāļāļģāļāļ§āļ % āļāļēāļĢāļัāļāđāļŦāļĨāļ
xhr.upload.onprogress = (e) => {
if (e.lengthComputable) onProgress(Math.round((e.loaded / e.total) * 100));
};
xhr.onload = () => resolve(JSON.parse(xhr.responseText).id); // āļŠ่āļāļืāļ File ID
xhr.send(reader.result); // āļŠ่āļāļ้āļāļĄูāļĨāđāļāļĨ์
});
});
}
2️⃣ āļั่āļ Server (Code.gs)
āļั่āļ Server āļāļģāļāļēāļāđāļāļēāļĄāļēāļ āļĄีāļŦāļ้āļēāļี่āđāļ่ 2 āļāļĒ่āļēāļ:
āļัāļāļ์āļัāļ 1: getAuthToken (āļāļāđāļāļāļุāļāđāļ)
function getAuthToken() {
// āļāļ Token āļāļāļ User āļัāļāļุāļัāļāđāļื่āļāļŠ่āļāđāļŦ้ Client āđāļ้āļĒิāļ API
return ScriptApp.getOAuthToken();
}
āļัāļāļ์āļัāļ 2: _processUploadedFile (āļāļāļัāļāļāļāļāđāļ้āļēāļั้āļ) āđāļĄื่āļ Client āļัāļāđāļŦāļĨāļāđāļŠāļĢ็āļāđāļĨāļ°āđāļ้ File ID āļĄāļēāđāļĨ้āļ§ Server āļāļ°āļĢัāļ ID āļั้āļāļĄāļē "āļĒ้āļēāļĒāļี่" āđāļĨāļ° "āđāļāļĨี่āļĒāļāļื่āļ"
function _processUploadedFile(fileId, targetFolder, newNamePrefix) {
var file = DriveApp.getFileById(fileId); // āļัāļāđāļāļĨ์āļ้āļ§āļĒ ID
// āđāļāļĨี่āļĒāļāļื่āļāđāļāļĨ์ (āđāļ่āļ āđāļิāļĄ Prefix Cover_)
if (newNamePrefix) {
file.setName(newNamePrefix + "_" + file.getName());
}
// āļั้āļāļ่āļēāđāļŦ้āđāļāļĢāļ็āđāļ้āļี่āļĄีāļĨิāļāļ์āļูāđāļ้ (āļŠāļģāļัāļāļĄāļēāļāļŠāļģāļŦāļĢัāļāļāļēāļĢāđāļŠāļāļāļāļĨ)
file.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW);
// āļĒ้āļēāļĒāđāļ้āļē Folder āļี่āļูāļāļ้āļāļ
if (targetFolder) {
file.moveTo(targetFolder);
}
return file.getId();
}
ð āļŠāļĢุāļāļ้āļāļีāļāļāļāļĢāļ°āļāļāļี้ (āļāļģāđāļĄāļึāļ Pro?)
1. No Size Limit (Virtually): āļุāļāļŠāļēāļĄāļēāļĢāļāļัāļāđāļŦāļĨāļāđāļāļĨ์āļāļāļēāļ 100MB āļŦāļĢืāļ 500MB āđāļ้āļŠāļāļēāļĒāđ āđāļāļĢāļēāļ°āđāļĄ่āđāļ้āļ§ิ่āļāļ่āļēāļ Server āļāļāļ Apps Script
2. No Timeout: āļāļēāļĢāļัāļāđāļŦāļĨāļāđāļิāļāļึ้āļāļĢāļ°āļŦāļ§่āļēāļ Browser āļัāļ Google Drive āđāļāļĒāļāļĢāļ āļāļģāđāļŦ้ GAS āđāļĄ่āļัāļāļāļēāļĢāļāļģāļāļēāļāđāļĄ้āļāļ°āļัāļāđāļŦāļĨāļāļāļēāļāđāļิāļ 6 āļāļēāļี
3. Real Progress Bar: āļŠāļēāļĄāļēāļĢāļāđāļŠāļāļ % āļāļēāļĢāļัāļāđāļŦāļĨāļāđāļŦ้āļู้āđāļ้āđāļŦ็āļāđāļ้āļāļĢิāļ (āđāļāļĢāļēāļ°āđāļ้ XMLHttpRequest āļั่āļ Client) āļึ่āļ google.script.run āļāļģāđāļĄ่āđāļ้
4. Server Load Reduced: Server āđāļĄ่āļ้āļāļāļāļĢāļ°āļĄāļ§āļĨāļāļĨāđāļāļĨ์ Blob āļāļģāđāļŦ้āļĢāļāļāļĢัāļāļู้āđāļ้āļāļēāļāļāļĢ้āļāļĄāļัāļāđāļ้āļĄāļēāļāļึ้āļ
āļี่āļืāļāļŠāļāļēāļัāļāļĒāļāļĢāļĢāļĄāļāļēāļĢāļัāļāđāļŦāļĨāļāļี่āļีāļี่āļŠุāļāđāļ่āļēāļี่āļāļ°āļāļģāđāļ้āļāļ Google Apps Script āđāļāļัāļāļุāļัāļāļāļĢัāļ