วิธีใช้ App Engine Blobstore (โมดูล 15)

1. ภาพรวม

ชุดโปรแกรม Codelab สำหรับการย้ายข้อมูลแบบ Serverless (บทแนะนำแบบลงมือทำด้วยตนเอง) และวิดีโอที่เกี่ยวข้องมีจุดประสงค์เพื่อช่วยให้นักพัฒนาแอป Google Cloud Serverless ปรับการดำเนินการให้ทันสมัยได้ด้วยคำแนะนำการย้ายข้อมูลอย่างน้อย 1 รายการ โดยให้ย้ายออกจากบริการเดิมเป็นหลัก การดำเนินการดังกล่าวทำให้แอปพกพาไปได้ทุกที่ รวมถึงมอบตัวเลือกและความยืดหยุ่นที่มากขึ้น ทำให้สามารถผสานรวมและเข้าถึงผลิตภัณฑ์ Cloud ที่หลากหลายยิ่งขึ้น และอัปเกรดเป็นรุ่นภาษาใหม่ๆ ได้ง่ายยิ่งขึ้น แม้ว่าในช่วงแรกจะมุ่งเน้นที่ผู้ใช้ Cloud รุ่นแรกสุด ซึ่งเป็นนักพัฒนา App Engine (สภาพแวดล้อมมาตรฐาน) เป็นหลัก แต่ชุดโซลูชันนี้ก็กว้างพอที่จะรวมแพลตฟอร์มแบบ Serverless อื่นๆ เช่น Cloud Functions และ Cloud Run หรือแพลตฟอร์มอื่นๆ ที่เกี่ยวข้อง

Codelab ของโมดูล 15 นี้อธิบายถึงวิธีเพิ่มการใช้งาน App Engine blobstore ไปยังแอปตัวอย่างจากโมดูล 0 จากนั้นคุณจึงจะพร้อมย้ายข้อมูลการใช้งานดังกล่าวไปยัง Cloud Storage ในโมดูล 16 ถัดไป

คุณจะได้เรียนรู้วิธีต่อไปนี้

  • เพิ่มการใช้ API/ไลบรารี App Engine Blobstore
  • จัดเก็บการอัปโหลดของผู้ใช้ไปยังบริการ blobstore
  • เตรียมการสำหรับขั้นตอนถัดไปในการย้ายข้อมูลไปยัง Cloud Storage

สิ่งที่ต้องมี

แบบสำรวจ

คุณจะใช้บทแนะนำนี้อย่างไร

อ่านเท่านั้น อ่านและทำแบบฝึกหัด

คุณจะให้คะแนนประสบการณ์การใช้งาน 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 ที่ใช้งานได้ หากยังไม่มี คุณสามารถรับได้จากโมดูล 0 "START" (ลิงก์ด้านล่าง) Codelab นี้จะแนะนำคุณทีละขั้นตอน สรุปด้วยโค้ดที่คล้ายกับสิ่งที่อยู่ในโมดูล 15 "FINISH" โฟลเดอร์

ไดเรกทอรีของไฟล์ STARTing โมดูล 0 ควรมีลักษณะดังนี้

$ ls
README.md               index.html
app.yaml                main.py

3. (อีกครั้ง) ทำให้แอปพื้นฐานใช้งานได้

ขั้นตอนก่อนการทำงานที่เหลือของคุณที่ต้องดำเนินการตอนนี้มีดังนี้

  1. ทำความคุ้นเคยกับเครื่องมือบรรทัดคำสั่ง gcloud
  2. ทำให้แอปตัวอย่างใช้งานได้อีกครั้งกับ gcloud app deploy
  3. ยืนยันว่าแอปทำงานบน App Engine โดยไม่มีปัญหา

เมื่อคุณดำเนินการขั้นตอนเหล่านี้เสร็จเรียบร้อยและเห็นว่าเว็บแอปทำงานแล้ว (โดยมีเอาต์พุตคล้ายกับด้านล่าง) คุณก็พร้อมที่จะเพิ่มการแคชในแอปแล้ว

a7a9d2b80d706a2b.png

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 สิ่งที่จะเปลี่ยนแปลงมีดังนี้

  1. วัตถุประสงค์ของโมดูล os คือการสร้างชื่อพาธไฟล์ไปยังเทมเพลต Django เนื่องจากเรากำลังจะเปลี่ยนไปใช้ Jinja2 ที่จัดการปัญหานี้ จึงไม่จำเป็นต้องใช้ os รวมถึงตัวแสดงผลเทมเพลต Django ชื่อ google.appengine.ext.webapp.template อีกต่อไป เราจึงจะนำออก
  2. นำเข้า Blobstore API: google.appengine.ext.blobstore
  3. นำเข้าเครื่องจัดการ Blobstore ที่พบในเฟรมเวิร์ก webapp ดั้งเดิม ซึ่งไม่มีอยู่ใน webapp2: google.appengine.ext.webapp.blobstore_handlers
  4. นำเข้าการสนับสนุน 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)

ด้านล่างนี้เป็นภาพประกอบการเปลี่ยนแปลงต่างๆ ที่เกิดขึ้นจนถึงปัจจุบัน

2270783776759f7f.png

รองรับการอัปโหลดไฟล์

การเปลี่ยนแปลงที่สำคัญที่สุดของฟังก์ชันการทำงานคือการรองรับการอัปโหลดไฟล์ ไม่ว่าจะเป็นการแจ้งให้ผู้ใช้อัปโหลดไฟล์ การรองรับ "การข้าม" หรือแสดงผลไฟล์ที่เกี่ยวข้องกับการเข้าชม ทั้งหมดนั้นเป็นส่วนหนึ่งของภาพ การเปลี่ยนแปลงที่จำเป็นต่อการรองรับการอัปโหลดไฟล์มีดังนี้

  1. คำขอตัวแฮนเดิลหลัก GET จะไม่ดึงข้อมูลการเข้าชมล่าสุดเพื่อแสดงอีกต่อไป แต่จะแจ้งให้ผู้ใช้อัปโหลดแทน
  2. เมื่อผู้ใช้ปลายทางส่งไฟล์เพื่ออัปโหลดหรือข้ามกระบวนการนั้น POST จากแบบฟอร์มจะส่งตัวควบคุมไปยัง UploadHandler ใหม่ ซึ่งได้มาจาก google.appengine.ext.webapp.blobstore_handlers.BlobstoreUploadHandler
  3. เมธอด POST ของ UploadHandler จะทำการอัปโหลด เรียก store_visit() เพื่อลงทะเบียนการเข้าชม และเรียกใช้การเปลี่ยนเส้นทาง HTTP 307 เพื่อส่งผู้ใช้กลับไปที่ "/" โดยที่...
  4. ระบบจะค้นหาเมธอด POST ของตัวแฮนเดิลหลักสำหรับ (ผ่าน fetch_visits()) และแสดงการเข้าชมล่าสุด หากผู้ใช้เลือก "ข้าม" ไม่มีการอัปโหลดไฟล์ แต่การเข้าชมจะยังคงได้รับการลงทะเบียน ตามด้วยการเปลี่ยนเส้นทางเดียวกัน
  5. การแสดงการเข้าชมล่าสุดจะมีฟิลด์ใหม่ที่แสดงให้ผู้ใช้เห็น ซึ่งอาจเป็น "มุมมอง" แบบไฮเปอร์ลิงก์ หากมีไฟล์ที่อัปโหลดอยู่หรือ "ไม่มี" หรือไม่เช่นนั้น การเปลี่ยนแปลงเหล่านี้เกิดขึ้นในเทมเพลต HTML พร้อมกับการเพิ่มแบบฟอร์มการอัปโหลด (จะมีการดูข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้ในเร็วๆ นี้)
  6. หากผู้ใช้ปลายทางคลิก "ดู" ลิงก์ของการเข้าชมที่มีวิดีโอที่อัปโหลดจะส่งคำขอ GET ไปยัง ViewBlobHandler ใหม่ ซึ่งได้มาจาก google.appengine.ext.webapp.blobstore_handlers.BlobstoreDownloadHandler โดยอาจแสดงผลไฟล์หากเป็นรูปภาพ (ในเบราว์เซอร์ หากมีการรองรับ) แจ้งให้ดาวน์โหลดหากไม่พบ หรือแสดงผลข้อผิดพลาด HTTP 404 หากไม่พบ
  7. นอกเหนือจากคลาสของตัวแฮนเดิลคู่ใหม่และเส้นทางคู่ใหม่เพื่อส่งการรับส่งข้อมูลไป ตัวแฮนเดิลหลักต้องใช้เมธอด 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 ในรูปแบบ POST โดยเรียกใช้ตัวแฮนเดิลการอัปโหลดเพื่อส่งไฟล์ไปยัง Blobstore
  • ในอีก UploadHandler.post มีการโทรไปยัง blobstore_handlers.BlobstoreUploadHandler.get_uploads นี่คือเวทมนตร์ที่แท้จริงในการนำไฟล์ไปไว้ใน Blobstore และแสดงผลรหัสถาวรที่ไม่ซ้ำกันสำหรับไฟล์นั้น ซึ่งก็คือ BlobKey
  • ในเดือนViewBlobHandler.get การเรียกใช้ blobstore_handlers.BlobstoreDownloadHandler.send ด้วย BlobKey ของไฟล์จะส่งผลให้ระบบดึงข้อมูลไฟล์และส่งต่อไปยังเบราว์เซอร์ของผู้ใช้ปลายทาง

การเรียกเหล่านี้หมายถึงการเข้าถึงฟีเจอร์ที่เพิ่มลงในแอปจำนวนมาก นี่คือภาพของการเปลี่ยนแปลงชุดที่ 2 และชุดสุดท้ายใน main.py

da2960525ac1b90d.png

อัปเดตเทมเพลต HTML

การอัปเดตแอปพลิเคชันหลักบางอย่างจะส่งผลต่ออินเทอร์เฟซผู้ใช้ (UI) ของแอป จึงต้องทำการเปลี่ยนแปลงที่เกี่ยวข้องกันในเทมเพลตเว็บ ซึ่งที่จริงแล้วมี 2 อย่างคือ

  1. แบบฟอร์มอัปโหลดไฟล์จำเป็นต้องมีองค์ประกอบอินพุต 3 อย่าง ได้แก่ ไฟล์ 1 ปุ่ม และปุ่มส่ง 2 ปุ่ม สำหรับอัปโหลดไฟล์และข้าม ตามลำดับ
  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

8583e975f25aa9e7.png

การเปลี่ยนแปลงสุดท้ายอย่างหนึ่งคือ Jinja2 ต้องการเทมเพลตในโฟลเดอร์ templates ดังนั้นให้สร้างโฟลเดอร์นั้นและย้าย index.html เข้าไปใหม่ การย้ายครั้งสุดท้ายนี้ถือว่าคุณทำการเปลี่ยนแปลงที่จำเป็นทั้งหมดสำหรับการเพิ่มการใช้ Blobstore ในแอปตัวอย่างโมดูล 0 แล้ว

(ไม่บังคับ) "การเพิ่มประสิทธิภาพ" Cloud Storage

ต่อมา พื้นที่เก็บข้อมูลของ Blobstore ได้กลายเป็น Cloud Storage เอง ซึ่งหมายความว่าการอัปโหลด Blobstore จะปรากฏใน Cloud Console โดยเฉพาะเบราว์เซอร์ Cloud Storage คำถามก็คือตำแหน่ง คำตอบคือที่เก็บข้อมูล Cloud Storage เริ่มต้นของแอป App Engine ชื่อนี้คือชื่อโดเมนแบบเต็มของแอป App Engine PROJECT_ID.appspot.com ซึ่งสะดวกมากเพราะรหัสโปรเจ็กต์ทั้งหมดจะไม่ซ้ำกันใช่ไหม

การอัปเดตที่ทำกับแอปพลิเคชันตัวอย่างจะใส่ไฟล์ที่อัปโหลดลงในที่เก็บข้อมูล แต่นักพัฒนาซอฟต์แวร์มีตัวเลือกในการเลือกตำแหน่งที่เฉพาะเจาะจงมากขึ้นได้ ที่เก็บข้อมูลเริ่มต้นจะเข้าถึงได้โดยใช้โปรแกรมผ่าน google.appengine.api.app_identity.get_default_gcs_bucket_name() ซึ่งจำเป็นต้องมีการนำเข้าใหม่หากคุณต้องการเข้าถึงค่านี้ เช่น ใช้เป็นคำนำหน้าสำหรับการจัดระเบียบไฟล์ที่อัปโหลด เช่น การจัดเรียงตามประเภทไฟล์ดังนี้

f61f7a23a1518705.png

ตัวอย่างเช่น หากต้องการใช้งานในลักษณะดังกล่าวกับรูปภาพ คุณจะมีโค้ดลักษณะนี้พร้อมกับโค้ดที่จะตรวจสอบประเภทไฟล์เพื่อเลือกชื่อที่เก็บข้อมูลที่ต้องการ

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 จัดเก็บการอัปโหลดใน "ราก" ที่เก็บข้อมูล (PROJECT_ID.appspot.com) เหมือนกับ main.py แต่จะมีโครงข่ายที่จำเป็นหากต้องการนำตัวอย่างมาใช้เพิ่มเติมตามที่แนะนำในส่วนนี้ ด้านล่างคือภาพของ "ความแตกต่าง" ระหว่าง main.py ถึง main-gcs.py

256e1ea68241a501.png

6. สรุป/ล้างข้อมูล

ส่วนนี้จะสรุป Codelab นี้โดยการทำให้แอปใช้งานได้ ซึ่งจะช่วยยืนยันว่าแอปทำงานได้ตามที่ต้องการและอยู่ในเอาต์พุตที่แสดงขึ้น หลังจากตรวจสอบแอปแล้ว ให้ทำตามขั้นตอนการทำความสะอาดและพิจารณาขั้นตอนถัดไป

ติดตั้งใช้งานและยืนยันแอปพลิเคชัน

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

f5b5f9f19d8ae978.pngจากนั้น ผู้ใช้ปลายทางจะอัปโหลดไฟล์แล้วคลิก "ส่ง" หรือคลิก "ข้าม" เพื่อไม่อัปโหลดอะไรเลย ในทั้ง 2 กรณี ผลการค้นหาคือหน้าจอการเข้าชมล่าสุด ซึ่งเสริมด้วย "มุมมอง" ลิงก์หรือ "ไม่มี" ระหว่างการประทับเวลาการเข้าชมกับข้อมูลผู้เข้าชม

f5ac6b98ee8a34cb.png

ขอแสดงความยินดีที่ Codelab นี้เสร็จสิ้นการเพิ่มการใช้ App Engine Blobstore ในแอปตัวอย่างโมดูล 0 ตอนนี้โค้ดควรตรงกับสิ่งที่อยู่ในโฟลเดอร์ FINISH (Module 15) มี main-gcs.py สำรองในโฟลเดอร์นั้นด้วย

ล้างข้อมูล

ทั่วไป

หากดำเนินการเสร็จแล้ว เราขอแนะนำให้คุณปิดใช้แอป App Engine เพื่อหลีกเลี่ยงการเรียกเก็บเงิน อย่างไรก็ตาม หากคุณต้องการทดสอบหรือทดลองเพิ่มเติม แพลตฟอร์ม App Engine จะมีโควต้าฟรี และตราบใดที่คุณใช้งานไม่เกินระดับการใช้งานดังกล่าว เราก็จะไม่เรียกเก็บเงิน ค่าดังกล่าวมีไว้สําหรับการประมวลผล แต่ก็อาจมีการเรียกเก็บเงินค่าบริการ App Engine ที่เกี่ยวข้องด้วย ดังนั้นโปรดดูข้อมูลเพิ่มเติมในหน้าราคา หากการย้ายข้อมูลนี้เกี่ยวข้องกับบริการระบบคลาวด์อื่นๆ ระบบจะเรียกเก็บเงินแยกต่างหาก ในทั้ง 2 กรณี หากมี โปรดดูส่วน "เฉพาะสำหรับ 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/images
  • console.cloud.google.com/storage/browser/staging.PROJECT_ID.appspot.com
  • ลิงก์พื้นที่เก็บข้อมูลด้านบนขึ้นอยู่กับ PROJECT_ID และ *LOC*ของคุณ เช่น "us" หากแอปของคุณโฮสต์ในสหรัฐอเมริกา

ในทางกลับกัน หากคุณไม่ต้องการดำเนินการกับแอปพลิเคชันนี้หรือ Codelab การย้ายข้อมูลอื่นๆ ที่เกี่ยวข้องต่อไป และต้องการลบทุกอย่างออกทั้งหมด ให้ปิดโปรเจ็กต์

เฉพาะสำหรับ Codelab นี้

บริการในรายการด้านล่างเป็นบริการเฉพาะสำหรับ Codelab นี้ โปรดดูข้อมูลเพิ่มเติมในเอกสารประกอบของผลิตภัณฑ์แต่ละรายการ

ขั้นตอนถัดไป

การย้ายข้อมูลเชิงตรรกะถัดไปที่ต้องพิจารณานั้นกล่าวถึงในโมดูล 16 ซึ่งจะแสดงให้นักพัฒนาแอปเห็นถึงวิธีย้ายข้อมูลจากบริการ App Engine Blobstore เพื่อใช้ไลบรารีของไคลเอ็นต์ 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

Google Cloud

Python

วิดีโอ

ใบอนุญาต

ผลงานนี้ได้รับอนุญาตภายใต้ใบอนุญาตทั่วไปครีเอทีฟคอมมอนส์แบบระบุแหล่งที่มา 2.0