1. บทนำ
เป้าหมายของ Codelab นี้คือเพื่อให้คุณเข้าใจวิธีเขียน Cloud Function เพื่อโต้ตอบกับการอัปโหลดไฟล์ CSV ไปยัง Cloud Storage เพื่ออ่านเนื้อหาและใช้อัปเดตสเปรดชีตใน Google ชีตโดยใช้ API ของชีต
การกระทำนี้อาจมองว่าเป็นการทำงานอัตโนมัติของการ "นำเข้าเป็น CSV" ด้วยตนเอง ครั้งแรก ซึ่งทำให้มั่นใจว่าคุณสามารถวิเคราะห์ข้อมูล (ซึ่งอาจสร้างขึ้นโดยทีมอื่น) ในสเปรดชีตได้ในทันทีที่มีข้อมูล
นี่คือลักษณะของการติดตั้งใช้งาน :
2. การตั้งค่าและข้อกำหนด
การตั้งค่าสภาพแวดล้อมตามเวลาที่สะดวก
- ลงชื่อเข้าใช้ Cloud Console และสร้างโปรเจ็กต์ใหม่หรือใช้โปรเจ็กต์ที่มีอยู่ซ้ำ (หากยังไม่มีบัญชี Gmail หรือ G Suite คุณต้องสร้างบัญชี)
โปรดจดจำรหัสโปรเจ็กต์ ซึ่งเป็นชื่อที่ไม่ซ้ำกันในโปรเจ็กต์ Google Cloud ทั้งหมด (ชื่อด้านบนมีคนใช้แล้ว และจะใช้ไม่ได้ ขออภัย) และจะมีการอ้างอิงใน Codelab ว่า PROJECT_ID
ในภายหลัง
- ถัดไป คุณจะต้องเปิดใช้การเรียกเก็บเงินใน Cloud Console เพื่อใช้ทรัพยากร Google Cloud
การใช้งาน Codelab นี้น่าจะไม่มีค่าใช้จ่ายใดๆ หากมี ตรวจสอบว่าคุณได้ทำตามวิธีการใน "การล้างข้อมูล" ซึ่งจะแนะนำคุณเกี่ยวกับวิธีปิดทรัพยากรเพื่อไม่ให้มีการเรียกเก็บเงินนอกเหนือจากบทแนะนำนี้ ผู้ใช้ใหม่ของ Google Cloud จะมีสิทธิ์เข้าร่วมโปรแกรมทดลองใช้ฟรี$300 USD
3. สร้างและกำหนดค่าสเปรดชีตใน Google ชีตและเปิดใช้ API
ขั้นแรก ให้สร้างเอกสารชีตใหม่ (ใช้ชีตนี้ของผู้ใช้ทุกคนได้) เมื่อสร้างแล้ว ให้จดจำตัวระบุ จะใช้เป็นตัวแปรสภาพแวดล้อมสำหรับฟังก์ชันที่เราจะเขียน :
จากคอนโซล GCP ให้เปิดใช้ Google ชีต API ในโปรเจ็กต์ที่สร้างขึ้นใหม่โดยไปที่ "API และบริการ" แล้วคลิก "ไลบรารี API" หัวข้อ :
ใน "IAM และ ผู้ดูแลระบบ" ให้ไปที่ "บัญชีบริการ" และจดบันทึกอีเมลสำหรับบัญชีบริการเริ่มต้นของ App Engine ซึ่งควรอยู่ในรูปแบบ your-project-id@appspot.gserviceaccount.com
นอกจากนี้ คุณยังสร้างบัญชีบริการของคุณเองซึ่งมีไว้สำหรับการดำเนินการนี้โดยเฉพาะได้อีกด้วย
สุดท้าย เพียงให้สิทธิ์แก้ไขสเปรดชีตแก่บัญชีบริการนี้โดยใช้ "แชร์" ปุ่ม :
ซึ่งตอนนี้เราสามารถเขียน Cloud Function และกำหนดค่าเพื่อใช้บัญชีบริการนี้ได้ ไฟล์จะสามารถเขียนลงในเอกสารสเปรดชีตนี้ที่เราเพิ่งสร้างขึ้นมา
4. สร้างที่เก็บข้อมูลของพื้นที่เก็บข้อมูล
ลองสร้างที่เก็บข้อมูลที่ฟังก์ชันระบบคลาวด์ของเราจะตรวจสอบไฟล์ CSV ใหม่
ในคอนโซล ให้ใช้เมนูด้านซ้ายเพื่อไปยัง "พื้นที่เก็บข้อมูล"... :
... และสร้างที่เก็บข้อมูลใหม่ชื่อ csv2sheet-POSTFIX
(แทนที่ POSTFIX ด้วยค่าที่ไม่ซ้ำกัน) โดยตั้งค่าอื่นทั้งหมดเป็นค่าเริ่มต้น :
5. สร้าง Cloud Function
ตอนนี้เราสร้าง Cloud Function ชื่อ csv2sheet
ที่จะทริกเกอร์เมื่ออัปโหลดไฟล์ไปยังที่เก็บข้อมูล Cloud Storage ที่เฉพาะเจาะจงได้แล้ว โค้ดจะเขียนใน Node.js 8 ด้วยฟังก์ชัน async โดยใช้ตัวแก้ไขในบรรทัดใน Cloud Console โดยตรง ดังนี้
ตรวจสอบว่าได้ตั้งค่าทริกเกอร์เป็น "Cloud Storage" และเพื่อปรับชื่อที่เก็บข้อมูลให้ตรงกับชื่อที่คุณสร้างไว้ในขั้นตอนก่อนหน้า
อัปเดตจุดแรกเข้าสำหรับฟังก์ชันที่เราจะเขียนไปยัง csv2sheet
ด้วย :
เปลี่ยนเนื้อหาของฟังก์ชันเป็น
- ใช้ Cloud Storage API และ Sheets API
- ทำเครื่องหมายฟังก์ชัน
csv2sheet
เป็นasync
- รับการเก็บรักษา
fileName
จากข้อมูลเมตาของเหตุการณ์ Cloud Storage และตั้งชื่อสำหรับชีตใหม่ที่เราจะสร้าง :
const {google} = require("googleapis");
const {Storage} = require("@google-cloud/storage")
exports.csv2sheet = async (data, context) => {
var fileName = data.name;
// basic check that this is a *.csv file, etc...
if (!fileName.endsWith(".csv")) {
console.log("Not a .csv file, ignoring.");
return;
}
// define name of new sheet
const sheetName = fileName.slice(0, -4);
// TODO!
};
การใช้ async
ในที่นี้จำเป็นต้องมีเพื่อใช้ await
ซึ่งเราจะดูในอีกสักครู่
ตัวเลือกที่สำคัญสองสามตัวเลือกในการสร้างฟังก์ชันนี้ได้แก่ (คลิกลิงก์ "เพิ่มเติม" ที่ด้านล่างของหน้าจอ) :
- ใช้เมนูแบบเลื่อนลงเพื่อเลือกบัญชีบริการที่กล่าวถึงข้างต้น
- กำหนดตัวแปรสภาพแวดล้อมชื่อ
SPREADSHEET_ID
ซึ่งควรตรงกับเอกสารชีตที่สร้างไว้ก่อนหน้านี้ ดังนี้
ขั้นตอนสุดท้ายคือเนื้อหา package.json
ที่มี Cloud Storage API และ Google ชีต API เป็นทรัพยากร Dependency 2 อย่างที่เราจะใช้ (ใช้แท็บ PACKAGE.JSON ของเครื่องมือแก้ไขในบรรทัดของคอนโซล)
{
"name": "csv2sheet",
"version": "0.0.42",
"dependencies": {
"googleapis": "^51.0.0",
"@google-cloud/storage": "^5.0.1"
}
}
เมื่อกำหนดค่าทุกอย่างตามที่อธิบายไว้แล้ว ให้คลิก "สร้าง" ได้เลย หลังจากนั้นไม่นานระบบควรสร้างและติดตั้งใช้งานฟังก์ชัน
6. ตั้งค่าการตรวจสอบสิทธิ์และ Sheets API
ก่อนที่จะเขียนโค้ดเพิ่มเติมในฟังก์ชันระบบคลาวด์โดยใช้เครื่องมือแก้ไขในบรรทัด เราต้องบล็อกการสร้าง Google Client API ที่มีขอบเขตพื้นที่เก็บข้อมูลและชีตที่เหมาะสม (อย่าลืมว่าส่วนนี้เป็นส่วนหนึ่งของฟังก์ชัน async
)
ในเครื่องมือแก้ไขฟังก์ชันของคอนโซล ให้คลิก "แก้ไข" แล้วเพิ่มโค้ดต่อไปนี้ลงในเนื้อหาของฟังก์ชัน csv2sheet
:
// block on auth + getting the sheets API object
const auth = await google.auth.getClient({
scopes: [
"https://www.googleapis.com/auth/spreadsheets",
"https://www.googleapis.com/auth/devstorage.read_only"
]
});
จากนั้นเราจะสร้างไคลเอ็นต์ Sheets API ดังนี้
const sheetsAPI = google.sheets({version: 'v4', auth});
7. ใช้ Sheets API เพื่อสร้างชีตเปล่า
ด้วยไคลเอ็นต์ Sheets API เราสามารถสร้างชีตใหม่ง่ายๆ ในเอกสาร แต่ก่อนที่จะดำเนินการต่อ ต่อไปนี้เป็นหมายเหตุสั้นๆ เกี่ยวกับคำศัพท์
- สเปรดชีตเป็นเอกสารจริงและมีการอ้างอิงโดยตัวระบุ (ดังที่กล่าวถึงข้างต้นและมองเห็นได้ใน URL ของเอกสาร)
- ชีตเป็นแท็บหนึ่งในเอกสาร และสามารถอ้างอิงได้จากชื่อแท็บ (ชื่อแท็บ) หรือตัวระบุที่สร้างขึ้นเมื่อสร้างชีต
ด้วยเหตุนี้ เราจึงขอแนะนำฟังก์ชันที่ใช้ไคลเอ็นต์ Sheets API เพื่อสร้างชีตว่างที่ตำแหน่ง 2 (โดยทั่วไปจะอยู่หลัง "Sheet1") เริ่มต้น ซึ่งมี 26 คอลัมน์ 2,000 แถว โดยแถวแรกถูกตรึงไว้ (เพิ่มลงในฟังก์ชันโดยใช้เครื่องมือแก้ไขในบรรทัด)
function addEmptySheet(sheetsAPI, sheetName) {
return new Promise((resolve, reject) => {
const emptySheetParams = {
spreadsheetId: process.env.SPREADSHEET_ID,
resource: {
requests: [
{
addSheet: {
properties: {
title: sheetName,
index: 1,
gridProperties: {
rowCount: 2000,
columnCount: 26,
frozenRowCount: 1
}
}
}
}
]
}
};
sheetsAPI.spreadsheets.batchUpdate( emptySheetParams, function(err, response) {
if (err) {
reject("The Sheets API returned an error: " + err);
} else {
const sheetId = response.data.replies[0].addSheet.properties.sheetId;
console.log("Created empty sheet: " + sheetId);
resolve(sheetId);
}
}
);
});
}
โปรดทราบว่าเราต้องใช้ตัวแปรสภาพแวดล้อม SPREADSHEET_ID
ที่สร้างขึ้นก่อนหน้านี้แทนการฮาร์ดโค้ดการอ้างอิงไปยังสเปรดชีต
เราต้องจำ sheetId
สำหรับคำขอเพิ่มเติมที่ส่งไปยังชีตนี้ นอกจากนี้ ชื่อชีตจะต้องไม่ซ้ำกัน และจะสร้างไม่สำเร็จหากมีชีตชื่อ sheetName
อยู่แล้ว
ฟังก์ชัน batchUpdate
ใน Sheets API เป็นวิธีทั่วไปในการโต้ตอบกับเอกสาร และมีคำอธิบายอยู่ที่นี่
8. อ่านข้อมูลจากไฟล์ CSV สำหรับการจัดเก็บข้อมูล
ตอนนี้เรามีที่ที่จะถ่ายโอนข้อมูลแล้ว เรามาพัฒนาฟังก์ชันระบบคลาวด์เพิ่มเติมในตัวแก้ไขในบรรทัดและใช้ Cloud Storage API เพื่อดึงข้อมูลจริงจากไฟล์ที่อัปโหลดและจัดเก็บไว้ในสตริง
function readCSVContent(sheetsAPI, file, sheetName) {
return new Promise((resolve, reject) => {
const storage = new Storage();
let fileContents = new Buffer('');
storage.bucket(file.bucket).file(file.name).createReadStream()
.on('error', function(err) {
reject('The Storage API returned an error: ' + err);
})
.on('data', function(chunk) {
fileContents = Buffer.concat([fileContents, chunk]);
})
.on('end', function() {
let content = fileContents.toString('utf8');
console.log("CSV content read as string : " + content );
resolve(content);
});
});
}
9. ป้อนข้อมูลในชีตที่สร้างใหม่
ถึงเวลาเติมข้อมูลในชีตที่เราสร้างโดยใช้ API ไคลเอ็นต์ชีตเดียวกันและข้อมูลที่เพิ่งรวบรวม เราจะใช้โอกาสนี้เพิ่มการจัดรูปแบบให้กับคอลัมน์ของแผ่นงาน (การเปลี่ยนขนาดแบบอักษรของแถวบนสุดและทำให้เป็นตัวหนา) :
function populateAndStyle(sheetsAPI, theData, sheetId) {
return new Promise((resolve, reject) => {
// Using 'batchUpdate' allows for multiple 'requests' to be sent in a single batch.
// Populate the sheet referenced by its ID with the data received (a CSV string)
// Style: set first row font size to 11 and to Bold. Exercise left for the reader: resize columns
const dataAndStyle = {
spreadsheetId: process.env.SPREADSHEET_ID,
resource: {
requests: [
{
pasteData: {
coordinate: {
sheetId: sheetId,
rowIndex: 0,
columnIndex: 0
},
data: theData,
delimiter: ","
}
},
{
repeatCell: {
range: {
sheetId: sheetId,
startRowIndex: 0,
endRowIndex: 1
},
cell: {
userEnteredFormat: {
textFormat: {
fontSize: 11,
bold: true
}
}
},
fields: "userEnteredFormat(textFormat)"
}
}
]
}
};
sheetsAPI.spreadsheets.batchUpdate(dataAndStyle, function(err, response) {
if (err) {
reject("The Sheets API returned an error: " + err);
} else {
console.log(sheetId + " sheet populated with " + theData.length + " rows and column style set.");
resolve();
}
});
});
}
โค้ดนี้ควรจะถูกเพิ่มลงในฟังก์ชันระบบคลาวด์ของเรา ซึ่งตอนนี้เสร็จสมบูรณ์ 99%!
บันทึกวิธีการรวมข้อมูลและการจัดรูปแบบเป็น requests
หลายรายการไว้ในการเรียกใช้ batchUpdate
ของ Sheets API ครั้งเดียว ทำให้การอัปเดตมีประสิทธิผลและมีประสิทธิภาพมากยิ่งขึ้น
และโปรดทราบว่าเรากำหนดช่วงการแก้ไขที่ตรงกับขนาดของชีตที่เราสร้างขึ้น ซึ่งหมายความว่าเนื้อหาที่เกิน 26 คอลัมน์ (ค่า columnCount
ที่ใช้เมื่อสร้างชีต) จะล้มเหลวเมื่อใช้โค้ดนี้
หากทุกอย่างเป็นไปด้วยดี คุณสามารถดำเนินการต่อไปนี้
- บันทึกฟังก์ชันที่อัปเดตแล้ว
- วางไฟล์ CSV ลงในที่เก็บข้อมูล
- คุณจะเห็นป๊อปอัปข้อมูลที่เกี่ยวข้องในสเปรดชีตของคุณ
10. นำข้อมูลทุกอย่างมารวมกันแล้วทดสอบความลื่นไหล
การเรียกฟังก์ชันที่เราเพิ่งพูดถึงไปสามารถเรียกใช้เป็นการเรียกที่บล็อกติดต่อกันในฟังก์ชัน csv2sheet
เดิมได้ ดังนี้
const sheetId = await addEmptySheet(sheetsAPI, sheetName);
const theData = await readCSVContent(sheetsAPI, data, sheetName);
await populateAndStyle(sheetsAPI, theData, sheetId);
หากคุณต้องการซอร์สโค้ดของฟังก์ชันที่สมบูรณ์ สามารถอยู่ได้ที่นี่ (ซึ่งน่าจะง่ายกว่าถ้าทำทั้งหมดรวมกันเป็นชุดเดียว)
เมื่อทุกอย่างพร้อมแล้ว เพียงอัปโหลดไฟล์ CSV ไปยังที่เก็บข้อมูลที่ถูกต้อง แล้วระบบจะอัปเดตสเปรดชีตด้วยชีตใหม่ที่มีเนื้อหาของไฟล์ โปรดดูไฟล์ CSV ตัวอย่างหากคุณยังไม่มีไฟล์
ลองอัปโหลดไฟล์หลายๆ ไฟล์ลงในที่เก็บข้อมูลเพื่อดูว่าจะเกิดอะไรขึ้น
11. เท่านี้ก็เรียบร้อย ถึงเวลาทำลายโครงสร้างพื้นฐานแล้ว
พูดเล่นๆ นะ มันไม่มีโครงสร้างพื้นฐานให้ฉีกทิ้ง ทั้งหมดนี้ทำได้แบบ Serverless เลย!
คุณลบฟังก์ชันระบบคลาวด์และที่เก็บข้อมูลที่คุณสร้างไว้ หรือแม้แต่ทั้งโปรเจ็กต์ได้หากต้องการ
12. ขั้นตอนถัดไปคือ
Codelab นี้สรุปขั้นตอนการฟังการอัปโหลดไปยังที่เก็บข้อมูล Cloud Storage ใน Cloud Function เพื่ออัปเดตสเปรดชีตใน Google ชีตโดยใช้ API ที่เหมาะสม
ขั้นตอนการติดตามผลมีดังนี้ :
- ดูคำแนะนำวิธีใช้ Cloud Functions (รวมถึงแนวทางปฏิบัติแนะนำ)
- ทำตามบทแนะนำ Cloud Functions
- สำรวจ Google Sheets API เพิ่มเติม
หากคุณประสบปัญหากับ Codelab นี้ โปรดรายงานปัญหาโดยใช้ลิงก์ที่มุมล่างซ้าย
เรายินดีรับฟังความคิดเห็นของคุณ