1. ภาพรวม
ชุด Codelab ของ Serverless Migration Station (บทแนะนำแบบลงมือปฏิบัติจริงที่ทำตามได้ด้วยตนเอง) และวิดีโอที่เกี่ยวข้องมีจุดมุ่งหมายเพื่อช่วยให้นักพัฒนาแอป Google Cloud แบบไร้เซิร์ฟเวอร์ปรับปรุงแอปพลิเคชันให้ทันสมัยโดยแนะนำการย้ายข้อมูลอย่างน้อย 1 รายการ ซึ่งส่วนใหญ่เป็นการย้ายข้อมูลออกจากบริการเดิม การทำเช่นนี้จะทำให้แอปของคุณพกพาได้มากขึ้น และช่วยให้คุณมีตัวเลือกและความยืดหยุ่นมากขึ้น ซึ่งจะช่วยให้คุณผสานรวมและเข้าถึงผลิตภัณฑ์ระบบคลาวด์ที่หลากหลายยิ่งขึ้น รวมถึงอัปเกรดเป็นภาษาเวอร์ชันใหม่ๆ ได้ง่ายขึ้น แม้ว่าในตอนแรกจะมุ่งเน้นไปที่ผู้ใช้ Cloud รุ่นแรกๆ ซึ่งส่วนใหญ่เป็นนักพัฒนาซอฟต์แวร์ App Engine (สภาพแวดล้อมมาตรฐาน) แต่ชุดข้อมูลนี้ก็ครอบคลุมแพลตฟอร์มแบบไร้เซิร์ฟเวอร์อื่นๆ เช่น Cloud Functions และ Cloud Run หรือที่อื่นๆ หากเกี่ยวข้อง
Codelab โมดูลที่ 15 นี้อธิบายวิธีเพิ่มการใช้งาน App Engine blobstore ลงในแอปตัวอย่างจากโมดูลที่ 0 จากนั้นคุณก็พร้อมที่จะย้ายข้อมูลการใช้งานดังกล่าวไปยัง Cloud Storage ในโมดูลที่ 16
คุณจะได้เรียนรู้วิธีต่อไปนี้
- เพิ่มการใช้ App Engine Blobstore API/ไลบรารี
- จัดเก็บไฟล์ที่ผู้ใช้อัปโหลดไว้ในบริการ
blobstore - เตรียมพร้อมสำหรับขั้นตอนถัดไปในการย้ายข้อมูลไปยัง Cloud Storage
สิ่งที่คุณต้องมี
- โปรเจ็กต์ Google Cloud Platform ที่มีบัญชีสำหรับการเรียกเก็บเงินของ GCP ที่ใช้งานอยู่
- ทักษะ Python ขั้นพื้นฐาน
- มีความรู้พื้นฐานเกี่ยวกับคำสั่ง Linux ทั่วไป
- ความรู้พื้นฐานเกี่ยวกับการพัฒนาและการทําให้แอป App Engine ใช้งานได้
- แอป App Engine ของโมดูล 0 ที่ใช้งานได้ (ดาวน์โหลดจากที่เก็บ)
แบบสำรวจ
คุณจะใช้บทแนะนำนี้อย่างไร
คุณจะให้คะแนนประสบการณ์การใช้งาน Python เท่าใด
คุณจะให้คะแนนประสบการณ์การใช้บริการ Google Cloud เท่าใด
2. ฉากหลัง
หากต้องการย้ายข้อมูลจาก App Engine Blobstore API ให้เพิ่มการใช้งานลงในแอป App Engine ndb พื้นฐานที่มีอยู่จากโมดูล 0 แอปตัวอย่างจะแสดงการเข้าชม 10 ครั้งล่าสุดต่อผู้ใช้ เรากำลังแก้ไขแอปเพื่อแจ้งให้ผู้ใช้ปลายทางอัปโหลดอาร์ติแฟกต์ (ไฟล์) ที่สอดคล้องกับ "การเข้าชม" ของตน หากผู้ใช้ไม่ต้องการดำเนินการดังกล่าว ก็จะมีตัวเลือก "ข้าม" ไม่ว่าผู้ใช้จะตัดสินใจอย่างไรก็ตาม หน้าถัดไปจะแสดงผลลัพธ์เช่นเดียวกับแอปจากโมดูล 0 (และโมดูลอื่นๆ อีกมากมายในชุดนี้) เมื่อผสานรวม App Engine blobstore นี้แล้ว เราจะย้ายข้อมูลไปยัง Cloud Storage ใน Codelab ถัดไป (โมดูลที่ 16) ได้
App Engine ให้สิทธิ์เข้าถึงระบบเทมเพลต Django และ Jinja2 และสิ่งหนึ่งที่ทำให้ตัวอย่างนี้แตกต่างออกไป (นอกเหนือจากการเพิ่มสิทธิ์เข้าถึง Blobstore) คือการเปลี่ยนจากการใช้ Django ในโมดูล 0 ไปเป็น Jinja2 ในโมดูล 15 ขั้นตอนสำคัญในการปรับปรุงแอป App Engine ให้ทันสมัยคือการย้ายข้อมูลเฟรมเวิร์กเว็บจาก webapp2 ไปยัง Flask โดยตัวหลังใช้ Jinja2 เป็นระบบเทมเพลตเริ่มต้น เราจึงเริ่มย้ายไปในทิศทางนั้นด้วยการใช้ Jinja2 ขณะที่ยังคงใช้ webapp2 สำหรับการเข้าถึง Blobstore เนื่องจาก Flask ใช้ Jinja2 โดยค่าเริ่มต้น จึงไม่จำเป็นต้องทำการเปลี่ยนแปลงเทมเพลตในโมดูลที่ 16
3. การตั้งค่า/การเตรียมการ
ก่อนจะไปถึงส่วนหลักของบทแนะนำนี้ ให้ตั้งค่าโปรเจ็กต์ รับโค้ด และทำให้แอปพื้นฐานใช้งานได้เพื่อเริ่มต้นด้วยโค้ดที่ใช้งานได้
1. ตั้งค่าโปรเจ็กต์
หากคุณได้ติดตั้งใช้งานแอปโมดูล 0 แล้ว เราขอแนะนำให้ใช้โปรเจ็กต์ (และโค้ด) เดียวกันซ้ำ หรือจะสร้างโปรเจ็กต์ใหม่หรือใช้โปรเจ็กต์อื่นที่มีอยู่ซ้ำก็ได้ ตรวจสอบว่าโปรเจ็กต์มีบัญชีสำหรับการเรียกเก็บเงินที่ใช้งานอยู่และเปิดใช้ App Engine แล้ว
2. รับแอปตัวอย่างพื้นฐาน
ข้อกำหนดเบื้องต้นอย่างหนึ่งของ Codelab นี้คือการมีแอปตัวอย่างโมดูล 0 ที่ใช้งานได้ หากไม่มี คุณสามารถดาวน์โหลดได้จากโฟลเดอร์ "START" ของโมดูล 0 (ลิงก์ด้านล่าง) โค้ดแล็บนี้จะแนะนำแต่ละขั้นตอนให้คุณ โดยจะจบด้วยโค้ดที่คล้ายกับโค้ดในโฟลเดอร์ "FINISH" ของโมดูลที่ 15
- เริ่มต้น: โฟลเดอร์โมดูล 0 (Python 2)
- เสร็จสิ้น: โฟลเดอร์โมดูล 15 (Python 2)
- ที่เก็บทั้งหมด (เพื่อโคลนหรือดาวน์โหลดไฟล์ ZIP)
ไดเรกทอรีของไฟล์เริ่มต้นของโมดูล 0 ควรมีลักษณะดังนี้
$ ls README.md index.html app.yaml main.py
3. (อีกครั้ง) ทำให้แอปพื้นฐานใช้งานได้
ขั้นตอนการเตรียมความพร้อมที่เหลือที่คุณต้องดำเนินการตอนนี้มีดังนี้
- ทำความคุ้นเคยกับ
gcloudเครื่องมือบรรทัดคำสั่งอีกครั้ง - ทำให้แอปตัวอย่างใช้งานได้อีกครั้งด้วย
gcloud app deploy - ยืนยันว่าแอปทำงานใน App Engine ได้โดยไม่มีปัญหา
เมื่อทำตามขั้นตอนเหล่านั้นสำเร็จและเห็นว่าเว็บแอปทำงานได้ (โดยมีเอาต์พุตคล้ายกับด้านล่าง) คุณก็พร้อมที่จะเพิ่มการใช้แคชลงในแอปแล้ว

4. อัปเดตไฟล์การกำหนดค่า
app.yaml
การกำหนดค่าแอปพลิเคชันไม่มีการเปลี่ยนแปลงที่สำคัญ อย่างไรก็ตาม ดังที่ได้กล่าวไว้ก่อนหน้านี้ เราจะเปลี่ยนจากเทมเพลต Django (ค่าเริ่มต้น) ไปเป็น Jinja2 ดังนั้นหากต้องการเปลี่ยน ผู้ใช้ควรระบุ Jinja2 เวอร์ชันล่าสุดที่มีอยู่ในเซิร์ฟเวอร์ App Engine โดยเพิ่มลงในส่วนไลบรารีของบุคคลที่สามในตัวของ app.yaml
ก่อน:
runtime: python27
threadsafe: yes
api_version: 1
handlers:
- url: /.*
script: main.app
แก้ไขไฟล์ app.yaml โดยเพิ่มส่วน libraries ใหม่ดังที่เห็นที่นี่
หลังจากนั้น
runtime: python27
threadsafe: yes
api_version: 1
handlers:
- url: /.*
script: main.app
libraries:
- name: jinja2
version: latest
คุณไม่จำเป็นต้องอัปเดตไฟล์การกำหนดค่าอื่นๆ ดังนั้นเรามาดูไฟล์แอปพลิเคชันกันเลย
5. แก้ไขไฟล์แอปพลิเคชัน
การนำเข้าและการรองรับ Jinja2
การเปลี่ยนแปลงชุดแรกสำหรับ main.py ได้แก่ การเพิ่มการใช้ Blobstore API และการแทนที่เทมเพลต Django ด้วย Jinja2 สิ่งที่จะเปลี่ยนแปลงมีดังนี้
- วัตถุประสงค์ของโมดูล
osคือการสร้างเส้นทางไฟล์ไปยังเทมเพลต Django เนื่องจากเราจะเปลี่ยนไปใช้ Jinja2 ซึ่งจะจัดการเรื่องนี้ได้ จึงไม่จำเป็นต้องใช้osรวมถึงตัวแสดงเทมเพลต Django อย่างgoogle.appengine.ext.webapp.templateอีกต่อไป เราจึงจะนำออก - นำเข้า Blobstore API:
google.appengine.ext.blobstore - นำเข้าตัวแฮนเดิล Blobstore ที่พบในเฟรมเวิร์ก
webappเดิม เนื่องจากตัวแฮนเดิลเหล่านี้ไม่พร้อมใช้งานในwebapp2:google.appengine.ext.webapp.blobstore_handlers - นำเข้าการรองรับ Jinja2 จากแพ็กเกจ
webapp2_extras
ก่อน:
import os
import webapp2
from google.appengine.ext import ndb
from google.appengine.ext.webapp import template
ใช้การเปลี่ยนแปลงในรายการด้านบนโดยแทนที่ส่วนการนำเข้าปัจจุบันใน main.py ด้วยข้อมูลโค้ดด้านล่าง
หลังจากนั้น
import webapp2
from webapp2_extras import jinja2
from google.appengine.ext import blobstore, ndb
from google.appengine.ext.webapp import blobstore_handlers
หลังจากนำเข้าแล้ว ให้เพิ่มโค้ดมาตรฐานเพื่อรองรับการใช้ Jinja2 ตามที่กำหนดไว้ในwebapp2_extrasเอกสาร ข้อมูลโค้ดต่อไปนี้จะรวมคลาสตัวแฮนเดิลคำขอ webapp2 มาตรฐานเข้ากับฟังก์ชัน Jinja2 ดังนั้นให้เพิ่มบล็อกโค้ดนี้ลงใน main.py ทันทีหลังจากนำเข้า
class BaseHandler(webapp2.RequestHandler):
'Derived request handler mixing-in Jinja2 support'
@webapp2.cached_property
def jinja2(self):
return jinja2.get_jinja2(app=self.app)
def render_response(self, _template, **context):
self.response.write(self.jinja2.render_template(_template, **context))
เพิ่มการรองรับ Blobstore
ตัวอย่างนี้แตกต่างจากการย้ายข้อมูลอื่นๆ ในชุดนี้ที่เราจะคงฟังก์ชันการทำงานหรือเอาต์พุตของแอปตัวอย่างให้เหมือนกัน (หรือเกือบจะเหมือนกัน) โดยไม่มีการเปลี่ยนแปลง UX (มากนัก) เนื่องจากตัวอย่างนี้จะแตกต่างจากค่าเริ่มต้นมากกว่า เรากำลังอัปเดตแอปเพื่อขอให้ผู้ใช้ระบุอาร์ติแฟกต์ไฟล์เพื่อลงทะเบียนการเข้าชม แทนที่จะลงทะเบียนการเข้าชมใหม่ทันทีแล้วแสดง 10 รายการล่าสุด จากนั้นผู้ใช้ปลายทางจะอัปโหลดไฟล์ที่เกี่ยวข้องหรือเลือก "ข้าม" เพื่อไม่อัปโหลดอะไรเลยก็ได้ เมื่อดำเนินการขั้นตอนนี้เสร็จสมบูรณ์แล้ว ระบบจะแสดงหน้า "การเข้าชมล่าสุด"
การเปลี่ยนแปลงนี้ช่วยให้แอปของเราใช้บริการ Blobstore เพื่อจัดเก็บ (และอาจแสดงผลในภายหลัง) รูปภาพหรือไฟล์ประเภทอื่นๆ ในหน้าการเข้าชมล่าสุดได้
อัปเดตโมเดลข้อมูลและนำไปใช้
เราจะจัดเก็บข้อมูลเพิ่มเติม โดยเฉพาะการอัปเดตรูปแบบข้อมูลเพื่อจัดเก็บรหัส (เรียกว่า "BlobKey") ของไฟล์ที่อัปโหลดไปยัง Blobstore และเพิ่มการอ้างอิงเพื่อบันทึกไว้ใน store_visit() เนื่องจากระบบจะแสดงข้อมูลเพิ่มเติมนี้พร้อมกับข้อมูลอื่นๆ ทั้งหมดเมื่อมีการค้นหา fetch_visits() จึงยังคงเหมือนเดิม
ต่อไปนี้คือภาพก่อนและหลังการอัปเดตเหล่านี้ ซึ่งมี file_blob ซึ่งเป็น ndb.BlobKeyProperty
ก่อน:
class Visit(ndb.Model):
'Visit entity registers visitor IP address & timestamp'
visitor = ndb.StringProperty()
timestamp = ndb.DateTimeProperty(auto_now_add=True)
def store_visit(remote_addr, user_agent):
'create new Visit entity in Datastore'
Visit(visitor='{}: {}'.format(remote_addr, user_agent)).put()
def fetch_visits(limit):
'get most recent visits'
return Visit.query().order(-Visit.timestamp).fetch(limit)
หลังจากนั้น
class Visit(ndb.Model):
'Visit entity registers visitor IP address & timestamp'
visitor = ndb.StringProperty()
timestamp = ndb.DateTimeProperty(auto_now_add=True)
file_blob = ndb.BlobKeyProperty()
def store_visit(remote_addr, user_agent, upload_key):
'create new Visit entity in Datastore'
Visit(visitor='{}: {}'.format(remote_addr, user_agent),
file_blob=upload_key).put()
def fetch_visits(limit):
'get most recent visits'
return Visit.query().order(-Visit.timestamp).fetch(limit)
ภาพแสดงการเปลี่ยนแปลงที่เกิดขึ้นจนถึงตอนนี้มีดังนี้

รองรับการอัปโหลดไฟล์
การเปลี่ยนแปลงฟังก์ชันการทำงานที่สำคัญที่สุดคือการรองรับการอัปโหลดไฟล์ ไม่ว่าจะเป็นการแจ้งให้ผู้ใช้อัปโหลดไฟล์ การรองรับฟีเจอร์ "ข้าม" หรือการแสดงไฟล์ที่สอดคล้องกับการเข้าชม ทั้งหมดนี้เป็นส่วนหนึ่งของภาพ การเปลี่ยนแปลงที่จำเป็นต่อการรองรับการอัปโหลดไฟล์มีดังนี้
- ตัวแฮนเดิลหลัก
GETจะไม่ดึงข้อมูลการเข้าชมล่าสุดเพื่อแสดงอีกต่อไป แต่จะแจ้งให้ผู้ใช้อัปโหลดแทน - เมื่อผู้ใช้ปลายทางส่งไฟล์เพื่ออัปโหลดหรือข้ามกระบวนการดังกล่าว
POSTจากแบบฟอร์มจะส่งการควบคุมไปยังUploadHandlerใหม่ซึ่งได้มาจากgoogle.appengine.ext.webapp.blobstore_handlers.BlobstoreUploadHandler - เมธอด
POSTของUploadHandlerจะอัปโหลด เรียกใช้store_visit()เพื่อลงทะเบียนการเข้าชม และทริกเกอร์การเปลี่ยนเส้นทาง HTTP 307 เพื่อส่งผู้ใช้กลับไปที่ "/" ซึ่ง... - เมธอด
POSTของตัวแฮนเดิลหลักจะค้นหา (ผ่านfetch_visits()) และแสดงการเข้าชมล่าสุด หากผู้ใช้เลือก "ข้าม" ระบบจะไม่ทำการอัปโหลดไฟล์ แต่จะยังคงลงทะเบียนการเข้าชมและเปลี่ยนเส้นทางเดิม - การเข้าชมล่าสุดจะแสดงช่องใหม่ต่อผู้ใช้ ซึ่งอาจเป็น "ดู" ที่มีไฮเปอร์ลิงก์หากมีไฟล์ที่อัปโหลด หรือ "ไม่มี" ในกรณีอื่นๆ การเปลี่ยนแปลงเหล่านี้จะเกิดขึ้นในเทมเพลต HTML พร้อมกับการเพิ่มแบบฟอร์มอัปโหลด (ข้อมูลเพิ่มเติมจะแจ้งให้ทราบเร็วๆ นี้)
- หากผู้ใช้ปลายทางคลิกลิงก์ "ดู" สำหรับการเข้าชมที่มีวิดีโอที่อัปโหลด ระบบจะส่ง
GETคำขอไปยังViewBlobHandlerใหม่ที่ได้มาจากgoogle.appengine.ext.webapp.blobstore_handlers.BlobstoreDownloadHandlerโดยจะแสดงไฟล์หากเป็นรูปภาพ (ในเบราว์เซอร์หากรองรับ) แจ้งให้ดาวน์โหลดหากไม่รองรับ หรือแสดงข้อผิดพลาด HTTP 404 หากไม่พบ - นอกเหนือจากคู่คลาสตัวแฮนเดิลใหม่และคู่เส้นทางใหม่เพื่อส่งการเข้าชมไปยังคลาสเหล่านั้นแล้ว ตัวแฮนเดิลหลักยังต้องมีเมธอด
POSTใหม่เพื่อรับการเปลี่ยนเส้นทาง 307 ที่อธิบายไว้ข้างต้น
ก่อนการอัปเดตเหล่านี้ แอปโมดูล 0 จะมีเพียงตัวแฮนเดิลหลักที่มีเมธอด GET และเส้นทางเดียว
ก่อน:
class MainHandler(webapp2.RequestHandler):
'main application (GET) handler'
def get(self):
store_visit(self.request.remote_addr, self.request.user_agent)
visits = fetch_visits(10)
tmpl = os.path.join(os.path.dirname(__file__), 'index.html')
self.response.out.write(template.render(tmpl, {'visits': visits}))
app = webapp2.WSGIApplication([
('/', MainHandler),
], debug=True)
เมื่อใช้การอัปเดตดังกล่าวแล้ว ตอนนี้จะมีตัวแฮนเดิล 3 ตัว ได้แก่ 1) ตัวแฮนเดิลการอัปโหลดที่มีเมธอด POST 2) ตัวแฮนเดิลการดาวน์โหลด "ดู Blob" ที่มีเมธอด GET และ 3) ตัวแฮนเดิลหลักที่มีเมธอด GET และ POST ทำการเปลี่ยนแปลงเหล่านี้เพื่อให้ส่วนที่เหลือของแอปมีลักษณะเหมือนด้านล่าง
หลังจากนั้น
class UploadHandler(blobstore_handlers.BlobstoreUploadHandler):
'Upload blob (POST) handler'
def post(self):
uploads = self.get_uploads()
blob_id = uploads[0].key() if uploads else None
store_visit(self.request.remote_addr, self.request.user_agent, blob_id)
self.redirect('/', code=307)
class ViewBlobHandler(blobstore_handlers.BlobstoreDownloadHandler):
'view uploaded blob (GET) handler'
def get(self, blob_key):
self.send_blob(blob_key) if blobstore.get(blob_key) else self.error(404)
class MainHandler(BaseHandler):
'main application (GET/POST) handler'
def get(self):
self.render_response('index.html',
upload_url=blobstore.create_upload_url('/upload'))
def post(self):
visits = fetch_visits(10)
self.render_response('index.html', visits=visits)
app = webapp2.WSGIApplication([
('/', MainHandler),
('/upload', UploadHandler),
('/view/([^/]+)?', ViewBlobHandler),
], debug=True)
โค้ดที่เราเพิ่งเพิ่มมีฟังก์ชันที่สำคัญหลายอย่างดังนี้
- ใน
MainHandler.getมีการโทรหาblobstore.create_upload_urlการเรียกนี้จะสร้าง URL ที่แบบฟอร์มPOSTs ไปยัง โดยเรียกตัวแฮนเดิลการอัปโหลดเพื่อส่งไฟล์ไปยัง Blobstore - ใน
UploadHandler.postมีการโทรหาblobstore_handlers.BlobstoreUploadHandler.get_uploadsนี่คือเคล็ดลับที่แท้จริงในการใส่ไฟล์ลงใน Blobstore และส่งคืนรหัสที่ไม่ซ้ำกันและถาวรสำหรับไฟล์นั้น ซึ่งก็คือBlobKey - ใน
ViewBlobHandler.getการเรียกใช้blobstore_handlers.BlobstoreDownloadHandler.sendด้วยBlobKeyของไฟล์จะทำให้ระบบดึงข้อมูลไฟล์และส่งต่อไฟล์ไปยังเบราว์เซอร์ของผู้ใช้ปลายทาง
การเรียกเหล่านี้แสดงถึงการเข้าถึงฟีเจอร์ส่วนใหญ่ที่เพิ่มลงในแอป รูปภาพต่อไปนี้แสดงการเปลี่ยนแปลงชุดที่ 2 และชุดสุดท้ายของ main.py

อัปเดตเทมเพลต HTML
การอัปเดตบางอย่างในแอปพลิเคชันหลักจะส่งผลต่ออินเทอร์เฟซผู้ใช้ (UI) ของแอป ดังนั้นจึงต้องมีการเปลี่ยนแปลงที่สอดคล้องกันในเทมเพลตเว็บ ซึ่งมี 2 รายการ ได้แก่
- ต้องมีแบบฟอร์มอัปโหลดไฟล์ที่มีองค์ประกอบอินพุต 3 รายการ ได้แก่ ไฟล์และปุ่มส่ง 2 ปุ่มสำหรับการอัปโหลดไฟล์และข้ามตามลำดับ
- อัปเดตเอาต์พุตการเข้าชมล่าสุดโดยเพิ่มลิงก์ "ดู" สำหรับการเข้าชมที่มีการอัปโหลดไฟล์ที่เกี่ยวข้อง หรือ "ไม่มี" ในกรณีอื่นๆ
ก่อน:
<!doctype html>
<html>
<head>
<title>VisitMe Example</title>
<body>
<h1>VisitMe example</h1>
<h3>Last 10 visits</h3>
<ul>
{% for visit in visits %}
<li>{{ visit.timestamp.ctime }} from {{ visit.visitor }}</li>
{% endfor %}
</ul>
</body>
</html>
ใช้การเปลี่ยนแปลงในรายการด้านบนเพื่อสร้างเทมเพลตที่อัปเดตแล้ว
หลังจากนั้น
<!doctype html>
<html>
<head>
<title>VisitMe Example</title>
<body>
<h1>VisitMe example</h1>
{% if upload_url %}
<h3>Welcome... upload a file? (optional)</h3>
<form action="{{ upload_url }}" method="POST" enctype="multipart/form-data">
<input type="file" name="file"><p></p>
<input type="submit"> <input type="submit" value="Skip">
</form>
{% else %}
<h3>Last 10 visits</h3>
<ul>
{% for visit in visits %}
<li>{{ visit.timestamp.ctime() }}
<i><code>
{% if visit.file_blob %}
(<a href="/view/{{ visit.file_blob }}" target="_blank">view</a>)
{% else %}
(none)
{% endif %}
</code></i>
from {{ visit.visitor }}
</li>
{% endfor %}
</ul>
{% endif %}
</body>
</html>
รูปภาพนี้แสดงการอัปเดตที่จำเป็นสำหรับ index.html

การเปลี่ยนแปลงสุดท้ายคือ Jinja2 ต้องการให้เทมเพลตอยู่ในโฟลเดอร์ templates ดังนั้นให้สร้างโฟลเดอร์ดังกล่าวแล้วย้าย index.html ไปไว้ในโฟลเดอร์นั้น การย้ายข้อมูลครั้งสุดท้ายนี้จะทำให้คุณทำการเปลี่ยนแปลงที่จำเป็นทั้งหมดสำหรับการเพิ่มการใช้ Blobstore ลงในแอปตัวอย่างโมดูล 0 เสร็จสมบูรณ์
(ไม่บังคับ) "การเพิ่มประสิทธิภาพ" ของ Cloud Storage
ในที่สุดที่เก็บข้อมูล Blobstore ก็พัฒนามาเป็น Cloud Storage ซึ่งหมายความว่าการอัปโหลด Blobstore จะปรากฏใน Cloud Console โดยเฉพาะในเบราว์เซอร์ Cloud Storage คำถามคือที่ไหน คำตอบคือ Bucket ของ Cloud Storage เริ่มต้นของแอป App Engine โดยมีชื่อเป็นชื่อโดเมนแบบเต็มของแอป App Engine PROJECT_ID.appspot.com ซึ่งสะดวกมากเนื่องจากรหัสโปรเจ็กต์ทั้งหมดไม่ซ้ำกันใช่ไหม
การอัปเดตที่ทำกับแอปพลิเคชันตัวอย่างจะวางไฟล์ที่อัปโหลดลงในที่เก็บข้อมูลนั้น แต่ผู้พัฒนาจะมีตัวเลือกในการเลือกตำแหน่งที่เฉพาะเจาะจงมากขึ้น เข้าถึงที่เก็บข้อมูลเริ่มต้นได้โดยอัตโนมัติผ่าน google.appengine.api.app_identity.get_default_gcs_bucket_name() ซึ่งต้องมีการนำเข้าใหม่หากต้องการเข้าถึงค่านี้ เช่น เพื่อใช้เป็นคำนำหน้าในการจัดระเบียบไฟล์ที่อัปโหลด ตัวอย่างเช่น การจัดเรียงตามประเภทไฟล์

หากต้องการใช้ฟีเจอร์ดังกล่าวกับรูปภาพ ตัวอย่างเช่น คุณจะต้องมีโค้ดแบบนี้พร้อมกับโค้ดที่ตรวจสอบประเภทไฟล์เพื่อเลือกชื่อที่ต้องการ
ROOT_BUCKET = app_identity.get_default_gcs_bucket_name()
IMAGE_BUCKET = '%s/%s' % (ROOT_BUCKET, 'images')
นอกจากนี้ คุณยังต้องตรวจสอบรูปภาพที่อัปโหลดโดยใช้เครื่องมือ เช่น โมดูล imghdr ของ Python Standard Library เพื่อยืนยันประเภทรูปภาพด้วย สุดท้ายนี้ คุณอาจต้องจำกัดขนาดของการอัปโหลดในกรณีที่มีผู้ไม่ประสงค์ดี
สมมติว่าคุณทำทุกอย่างเสร็จแล้ว เราจะอัปเดตแอปให้รองรับการระบุตำแหน่งที่จะจัดเก็บไฟล์ที่อัปโหลดได้อย่างไร เคล็ดลับคือการปรับแต่งการเรียกใช้ blobstore.create_upload_url ใน MainHandler.get เพื่อระบุตำแหน่งที่ต้องการใน Cloud Storage สำหรับการอัปโหลดโดยการเพิ่มพารามิเตอร์ gs_bucket_name ดังนี้
blobstore.create_upload_url('/upload', gs_bucket_name=IMAGE_BUCKET))
เนื่องจากการอัปเดตนี้เป็นแบบไม่บังคับในกรณีที่คุณต้องการระบุตำแหน่งที่ควรจะอัปโหลด จึงไม่ได้เป็นส่วนหนึ่งของmain.pyในที่เก็บ แต่คุณสามารถใช้ตัวเลือกอื่นที่ชื่อ main-gcs.py แทนได้ในรีวิวของคุณในที่เก็บ โค้ดใน main-gcs.py จะจัดเก็บข้อมูลที่อัปโหลดใน Bucket "รูท" (PROJECT_ID.appspot.com) เหมือนกับ main.py แต่จะจัดเตรียมโครงสร้างพื้นฐานที่คุณต้องการหากคุณจะดึงตัวอย่างไปใช้กับสิ่งอื่นตามที่ระบุไว้ในส่วนนี้ แทนที่จะใช้ "โฟลเดอร์" Bucket แยกต่างหาก ภาพด้านล่างแสดง "ความแตกต่าง" ระหว่าง main.py กับ main-gcs.py

6. สรุป/ล้างข้อมูล
ส่วนนี้จะสรุป Codelab นี้ด้วยการติดตั้งใช้งานแอป ยืนยันว่าแอปทำงานได้ตามที่ต้องการและในเอาต์พุตที่แสดง หลังจากตรวจสอบแอปแล้ว ให้ทำขั้นตอนการล้างข้อมูลและพิจารณาขั้นตอนถัดไป
ทำให้แอปพลิเคชันใช้งานได้และยืนยัน
นำแอปไปใช้งานอีกครั้งด้วย gcloud app deploy และยืนยันว่าแอปทำงานได้ตามที่โฆษณาไว้ โดยมีประสบการณ์ของผู้ใช้ (UX) แตกต่างจากแอปในโมดูล 0 ตอนนี้แอปของคุณมี 2 หน้าจอที่แตกต่างกัน โดยหน้าจอแรกคือข้อความแจ้งแบบฟอร์มการอัปโหลดไฟล์การเข้าชม
จากนั้น ผู้ใช้ปลายทางจะอัปโหลดไฟล์แล้วคลิก "ส่ง" หรือคลิก "ข้าม" เพื่อไม่ให้อัปโหลดอะไรก็ได้ ไม่ว่าจะในกรณีใด ผลลัพธ์คือหน้าจอการเข้าชมล่าสุด ซึ่งตอนนี้ได้รับการเพิ่มลิงก์ "ดู" หรือ "ไม่มี" ระหว่างการประทับเวลาการเข้าชมและข้อมูลผู้เข้าชม

ขอแสดงความยินดีที่ทำ Codelab นี้เสร็จสิ้นโดยเพิ่มการใช้ App Engine Blobstore ลงในแอปตัวอย่างของโมดูล 0 ตอนนี้โค้ดของคุณควรตรงกับโค้ดในโฟลเดอร์ FINISH (โมดูล 15) main-gcs.py ทางเลือกจะอยู่ในโฟลเดอร์นั้นด้วย
ล้างข้อมูล
ทั่วไป
หากคุณดำเนินการเสร็จแล้วในตอนนี้ เราขอแนะนำให้ปิดใช้แอป App Engine เพื่อไม่ให้มีการเรียกเก็บเงิน อย่างไรก็ตาม หากต้องการทดสอบหรือทดลองเพิ่มเติม แพลตฟอร์ม App Engine มีโควต้าฟรี ดังนั้นตราบใดที่คุณไม่เกินระดับการใช้งานดังกล่าว คุณก็จะไม่ถูกเรียกเก็บเงิน ซึ่งเป็นค่าบริการสำหรับการประมวลผล แต่อาจมีค่าบริการสำหรับบริการ App Engine ที่เกี่ยวข้องด้วย ดังนั้นโปรดดูข้อมูลเพิ่มเติมในหน้าการกำหนดราคา หากการย้ายข้อมูลนี้เกี่ยวข้องกับบริการอื่นๆ ของระบบคลาวด์ ระบบจะเรียกเก็บเงินสำหรับบริการเหล่านั้นแยกต่างหาก ไม่ว่าจะในกรณีใดก็ตาม หากมี ให้ดูส่วน "เฉพาะสำหรับ Codelab นี้" ด้านล่าง
เพื่อความโปร่งใสอย่างเต็มที่ การติดตั้งใช้งานแพลตฟอร์มการประมวลผลแบบ Serverless ของ Google Cloud เช่น App Engine จะทำให้เกิดค่าใช้จ่ายในการสร้างและพื้นที่เก็บข้อมูลเล็กน้อย Cloud Build มีโควต้าฟรีของตัวเอง เช่นเดียวกับ Cloud Storage การจัดเก็บรูปภาพนั้นจะใช้โควต้าบางส่วน อย่างไรก็ตาม คุณอาจอาศัยอยู่ในภูมิภาคที่ไม่มีระดับการใช้งานฟรีดังกล่าว ดังนั้นโปรดทราบการใช้พื้นที่เก็บข้อมูลเพื่อลดค่าใช้จ่ายที่อาจเกิดขึ้น "โฟลเดอร์" ของ Cloud Storage ที่คุณควรตรวจสอบ ได้แก่
console.cloud.google.com/storage/browser/LOC.artifacts.PROJECT_ID.appspot.com/containers/imagesconsole.cloud.google.com/storage/browser/staging.PROJECT_ID.appspot.com- ลิงก์พื้นที่เก็บข้อมูลด้านบนจะขึ้นอยู่กับ
PROJECT_IDและ *LOC*ation ของคุณ เช่น "us" หากแอปโฮสต์อยู่ในสหรัฐอเมริกา
ในทางกลับกัน หากคุณจะไม่ดำเนินการต่อกับแอปพลิเคชันนี้หรือ Codelab การย้ายข้อมูลอื่นๆ ที่เกี่ยวข้อง และต้องการลบทุกอย่างออกทั้งหมด ให้ปิดโปรเจ็กต์
เฉพาะสำหรับ Codelab นี้
บริการที่ระบุไว้ด้านล่างเป็นบริการเฉพาะสำหรับโค้ดแล็บนี้ ดูข้อมูลเพิ่มเติมได้ในเอกสารประกอบของแต่ละผลิตภัณฑ์
- บริการ Blobstore ของ App Engine อยู่ในโควต้าและขีดจำกัดของข้อมูลที่จัดเก็บ ดังนั้นโปรดอ่านข้อมูลดังกล่าว รวมถึงหน้าการกำหนดราคาสำหรับบริการแบบรวมแพ็กเกจเดิม
- บริการ App Engine Datastore ให้บริการโดย Cloud Datastore (Cloud Firestore ในโหมด Datastore) ซึ่งมีระดับฟรีด้วย ดูข้อมูลเพิ่มเติมได้ที่หน้าการกำหนดราคา
ขั้นตอนถัดไป
การย้ายข้อมูลเชิงตรรกะถัดไปที่ควรพิจารณาจะกล่าวถึงในโมดูลที่ 16 ซึ่งแสดงให้นักพัฒนาซอฟต์แวร์เห็นวิธีย้ายข้อมูลจากบริการ Blobstore ของ App Engine ไปใช้ไลบรารีของไคลเอ็นต์ Cloud Storage การอัปเกรดจะช่วยให้คุณเข้าถึงฟีเจอร์ Cloud Storage ได้มากขึ้น รวมถึงทำความคุ้นเคยกับไลบรารีของไคลเอ็นต์ที่ใช้ได้กับแอปภายนอก App Engine ไม่ว่าจะอยู่ใน Google Cloud, ระบบคลาวด์อื่นๆ หรือแม้แต่ในองค์กร หากคุณไม่คิดว่าจำเป็นต้องใช้ฟีเจอร์ทั้งหมดที่มีจาก Cloud Storage หรือกังวลเกี่ยวกับผลกระทบต่อต้นทุน คุณสามารถใช้ App Engine Blobstore ต่อไปได้
นอกจากโมดูลที่ 16 แล้ว ยังมีการย้ายข้อมูลอื่นๆ ที่เป็นไปได้อีกมากมาย เช่น Cloud NDB และ Cloud Datastore, Cloud Tasks หรือ Cloud Memorystore นอกจากนี้ยังมีการย้ายข้อมูลข้ามผลิตภัณฑ์ไปยัง Cloud Run และ Cloud Functions ด้วย ที่เก็บการย้ายข้อมูลมีตัวอย่างโค้ดทั้งหมด ลิงก์ไปยัง Codelab และวิดีโอทั้งหมดที่มีอยู่ รวมถึงคำแนะนำเกี่ยวกับการย้ายข้อมูลที่ควรพิจารณาและ "ลำดับ" การย้ายข้อมูลที่เกี่ยวข้อง
7. แหล่งข้อมูลเพิ่มเติม
ปัญหา/ความคิดเห็นเกี่ยวกับ Codelab
หากพบปัญหาเกี่ยวกับ Codelab นี้ โปรดค้นหาปัญหาของคุณก่อนที่จะยื่นเรื่อง ลิงก์สำหรับค้นหาและสร้างปัญหาใหม่
แหล่งข้อมูลเกี่ยวกับการย้ายข้อมูล
คุณจะเห็นลิงก์ไปยังโฟลเดอร์ที่เก็บสำหรับโมดูล 0 (START) และโมดูล 15 (FINISH) ในตารางด้านล่าง นอกจากนี้ คุณยังเข้าถึงได้จากที่เก็บสำหรับการย้ายข้อมูล Codelab ของ App Engine ทั้งหมด ซึ่งคุณสามารถโคลนหรือดาวน์โหลดไฟล์ ZIP ได้
Codelab | Python 2 | Python 3 |
โมดูล 0 | ไม่มี | |
โมดูล 15 (Codelab นี้) | ไม่มี |
แหล่งข้อมูลออนไลน์
ด้านล่างนี้คือแหล่งข้อมูลออนไลน์ที่อาจเกี่ยวข้องกับบทแนะนำนี้
App Engine
- บริการ Blobstore ของ App Engine
- โควต้าและขีดจำกัดของข้อมูลที่จัดเก็บใน App Engine
- เอกสารประกอบของ App Engine
- รันไทม์ของ App Engine (สภาพแวดล้อมมาตรฐาน) สำหรับ Python 2
- การใช้ไลบรารีในตัวของ App Engine ใน Python 2 App Engine
- ข้อมูลราคาและโควต้าของ App Engine
- เปิดตัวแพลตฟอร์ม App Engine รุ่นที่ 2 (2018)
- การเปรียบเทียบแพลตฟอร์มรุ่นที่ 1 และรุ่นที่ 2
- การสนับสนุนรันไทม์เวอร์ชันเดิมในระยะยาว
- ที่เก็บตัวอย่างการย้ายข้อมูลเอกสารประกอบ
- ที่เก็บตัวอย่างการย้ายข้อมูลที่ชุมชนมีส่วนร่วม
Google Cloud
- Python ใน Google Cloud Platform
- ไลบรารีของไคลเอ็นต์ Python สำหรับ Google Cloud
- ระดับ "ฟรีตลอด" ของ Google Cloud
- Google Cloud SDK (เครื่องมือบรรทัดคำสั่ง gcloud)
- เอกสารประกอบทั้งหมดของ Google Cloud
Python
- ระบบเทมเพลต Django และ Jinja2
webapp2เฟรมเวิร์กของเว็บwebapp2เอกสารประกอบwebapp2_extrasลิงก์webapp2_extrasเอกสารประกอบ Jinja2
วิดีโอ
- Serverless Migration Station
- Expeditions แบบ Serverless
- ติดตาม Google Cloud Tech
- ติดตาม Google Developers
ใบอนุญาต
ผลงานนี้ได้รับอนุญาตภายใต้สัญญาอนุญาตครีเอทีฟคอมมอนส์สำหรับยอมรับสิทธิของผู้สร้าง (Creative Commons Attribution License) 2.0 แบบทั่วไป