Ringkasan Blobstore API untuk layanan paket lama

Blobstore API memungkinkan aplikasi Anda menyajikan objek data, yang disebut blob, dengan ukuran yang jauh lebih besar dari ukuran yang diizinkan untuk objek dalam layanan Datastore. Blob berguna untuk menyajikan file besar, seperti file video atau gambar, dan untuk memungkinkan pengguna mengupload file data berukuran besar. Blob dibuat dengan mengupload file melalui permintaan HTTP. Biasanya, aplikasi Anda akan melakukannya dengan menampilkan formulir yang berisi kolom upload file kepada pengguna. Saat formulir dikirimkan, Blobstore akan membuat blob dari konten file dan menampilkan referensi tersembunyi ke blob, yang disebut kunci blob yang nantinya dapat digunakan untuk menyajikan blob. Aplikasi dapat menyajikan nilai blob lengkap sebagai respons terhadap permintaan pengguna, atau dapat membaca nilai secara langsung menggunakan antarmuka seperti file streaming.

Memperkenalkan Blobstore

Google App Engine mencakup layanan Blobstore, yang memungkinkan aplikasi menyajikan objek data yang hanya dibatasi oleh jumlah data yang dapat diupload atau didownload melalui satu koneksi HTTP. Objek ini disebut nilai Blobstore atau blob. Nilai Blobstore berfungsi sebagai respons dari pengendali permintaan dan dibuat sebagai file yang diupload melalui formulir web. Aplikasi tidak membuat data blob secara langsung; sebagai gantinya, blob dibuat secara tidak langsung oleh formulir web yang dikirimkan atau permintaan POST HTTP lainnya. Nilai Blobstore dapat disajikan kepada pengguna, atau diakses oleh aplikasi dalam aliran data seperti file, menggunakan Blobstore API.

Untuk meminta pengguna mengupload nilai Blobstore, aplikasi Anda harus menyajikan formulir web dengan kolom upload file. Aplikasi ini membuat URL tindakan formulir dengan memanggil Blobstore API. Browser pengguna mengupload file langsung ke Blobstore melalui URL yang dibuat. Blobstore kemudian menyimpan blob, menulis ulang permintaan untuk memuat kunci blob, dan meneruskannya ke jalur dalam aplikasi Anda. Pengendali permintaan di jalur tersebut dalam aplikasi Anda bisa melakukan pemrosesan formulir tambahan.

Untuk menyajikan blob, aplikasi Anda menetapkan header pada respons keluar, dan App Engine mengganti respons tersebut dengan nilai blob.

Blob tidak dapat diubah setelah dibuat, meskipun dapat dihapus. Setiap blob memiliki catatan info blob yang sesuai dan tersimpan di datastore, yang memberikan detail tentang blob, seperti waktu pembuatan dan jenis kontennya. Anda dapat menggunakan kunci blob untuk mengambil catatan info blob dan mengkueri propertinya.

Aplikasi dapat membaca nilai Blobstore sebagian dalam satu waktu menggunakan panggilan API. Ukuran bagian dapat mencapai ukuran maksimum dari nilai yang ditampilkan API. Ukurannya kurang dari 32 megabyte, yang ditampilkan di Python oleh konstanta google.appengine.ext.blobstore.MAX_BLOB_FETCH_SIZE. Aplikasi tidak dapat membuat atau mengubah nilai Blobstore, kecuali melalui file yang diupload oleh pengguna.

Menggunakan Blobstore

Aplikasi dapat menggunakan Blobstore untuk menerima file besar sebagai upload dari pengguna dan menyajikan file tersebut. File disebut blob setelah diupload. Aplikasi tidak mengakses blob secara langsung. Sebagai gantinya, aplikasi bekerja dengan blob melalui entity info blob (diwakili oleh class BlobInfo) di datastore.

Pengguna membuat blob dengan mengirimkan formulir HTML yang menyertakan satu atau beberapa kolom input file. Aplikasi Anda memanggil create_upload_url() untuk mendapatkan tujuan (tindakan) formulir ini, dengan meneruskan fungsi jalur URL pengendali di aplikasi Anda. Saat pengguna mengirimkan formulir, browser pengguna akan mengupload file yang ditentukan langsung ke Blobstore. Blobstore menulis ulang permintaan pengguna dan menyimpan data file yang diupload, mengganti data file yang diupload dengan satu atau beberapa kunci blob yang sesuai, lalu meneruskan permintaan yang ditulis ulang ke pengendali di jalur URL yang Anda berikan ke create_upload_url(). Pengendali ini dapat melakukan pemrosesan tambahan berdasarkan kunci blob.

Aplikasi ini dapat membaca bagian dari nilai Blobstore menggunakan antarmuka streaming yang mirip file. Lihat Class BlobReader.

Mengupload blob

Untuk membuat dan mengupload blob, ikuti prosedur berikut:

1. Membuat URL upload

Panggil blobstore.create_upload_url() guna membuat URL upload untuk formulir yang akan diisi pengguna, yang meneruskan jalur aplikasi yang akan dimuat saat POST formulir selesai.

upload_url = blobstore.create_upload_url("/upload_photo")

Ada versi asinkron, create_upload_url_async(). Fungsi ini memungkinkan kode aplikasi Anda tetap berjalan sementara Blobstore membuat URL upload.

2. Membuat formulir upload

Formulir harus menyertakan kolom upload file, dan enctype formulir harus ditetapkan ke multipart/form-data. Saat pengguna mengirimkan formulir, POST akan ditangani oleh Blobstore API, yang membuat blob. API ini juga membuat catatan info untuk blob dan menyimpan catatan di datastore, lalu meneruskan permintaan yang ditulis ulang ke aplikasi Anda di jalur yang diberikan sebagai kunci blob.

        # To upload files to the blobstore, the request method must be "POST"
        # and enctype must be set to "multipart/form-data".
        self.response.out.write(
            """
<html><body>
<form action="{0}" method="POST" enctype="multipart/form-data">
  Upload File: <input type="file" name="file"><br>
  <input type="submit" name="submit" value="Submit">
</form>
</body></html>""".format(
                upload_url
            )
        )

3. Menerapkan pengendali upload

Dalam pengendali ini, Anda dapat menyimpan kunci blob bersama model data aplikasi lainnya. Kunci blob itu sendiri tetap dapat diakses dari entity info blob di datastore. Perhatikan bahwa setelah pengguna mengirimkan formulir dan pengendali Anda dipanggil, blob telah disimpan dan info blob ditambahkan ke datastore. Jika aplikasi Anda tidak ingin menyimpan blob, Anda harus segera menghapus blob agar tidak menjadi telantar:

class PhotoUploadHandler(blobstore_handlers.BlobstoreUploadHandler):
    def post(self):
        upload = self.get_uploads()[0]
        user_photo = UserPhoto(
            user=users.get_current_user().user_id(), blob_key=upload.key()
        )
        user_photo.put()

        self.redirect("/view_photo/%s" % upload.key())

Webapp framework menyediakan class pengendali upload blobstore_handlers.BlobstoreUploadHandler untuk membantu Anda mengurai data formulir. Untuk informasi selengkapnya, lihat referensi untuk BlobstoreUploadHandler.

Saat Blobstore menulis ulang permintaan pengguna, bagian MIME dari file yang diupload akan dikosongkan, dan kunci blob ditambahkan sebagai header bagian MIME. Semua kolom dan bagian formulir lainnya akan dipertahankan dan diteruskan ke pengendali upload. Jika Anda tidak menentukan jenis konten, Blobstore akan mencoba menyimpulkannya dari ekstensi file. Jika tidak ada jenis konten yang dapat ditentukan, blob yang baru dibuat akan ditetapkan sebagai jenis konten application/octet-stream.

Menyajikan blob

Untuk menyajikan blob, Anda harus menyertakan pengendali download blob sebagai jalur dalam aplikasi Anda. Aplikasi menyajikan blob dengan menyetel header pada respons keluar. Contoh berikut menggunakan framework webapp. Saat menggunakan webapp, pengendali harus meneruskan kunci blob untuk blob yang diinginkan ke self.send_blob(). Dalam contoh ini, kunci blob diteruskan ke pengendali download sebagai bagian dari URL. Dalam praktiknya, pengendali download bisa mendapatkan kunci blob dengan cara apa pun yang Anda pilih, misalnya melalui metode lain atau tindakan pengguna.

class ViewPhotoHandler(blobstore_handlers.BlobstoreDownloadHandler):
    def get(self, photo_key):
        if not blobstore.get(photo_key):
            self.error(404)
        else:
            self.send_blob(photo_key)

Webapp framework menyediakan class pengendali download blobstore_handlers.BlobstoreDownloadHandler untuk membantu Anda mengurai data formulir. Untuk informasi selengkapnya, lihat referensi untuk BlobstoreDownloadHandler.

Blob dapat disajikan dari URL aplikasi apa pun. Untuk menyajikan blob dalam aplikasi Anda, tempatkan header khusus dalam respons yang berisi kunci blob. App Engine mengganti isi respons dengan konten blob.

Rentang byte blob

Blobstore mendukung penyajian sebagian dari nilai yang besar, bukan nilai penuh, sebagai respons atas permintaan. Untuk menyajikan nilai parsial, sertakan header X-AppEngine-BlobRange dalam respons keluar. Nilainya adalah rentang byte HTTP standar. Penomoran byte berbasis nol. X-AppEngine-BlobRange kosong menginstruksikan API untuk mengabaikan header rentang dan menyajikan blob lengkap. Contoh rentang mencakup:

  • 0-499 menyajikan 500 byte pertama dari nilai (0 hingga 499 byte, inklusif).
  • 500-999 menyajikan 500 byte dimulai dari byte 501.
  • 500- menyajikan semua byte yang dimulai dari byte 501 hingga akhir nilai.
  • -500 menyajikan 500 byte terakhir dari nilai.

Jika rentang byte valid untuk nilai Blobstore, Blobstore akan mengirimkan kode status 206 Partial Content dan rentang byte yang diminta ke klien. Jika rentang tidak valid untuk nilai tersebut, Blobstore akan mengirim 416 Requested Range Not Satisfiable.

Blobstore tidak mendukung beberapa rentang byte dalam satu permintaan (misalnya 100-199,200-299), terlepas dari apakah keduanya tumpang-tindih atau tidak.

Class webapp.blobstore_handlers.BlobstoreDownloadHandler menyertakan fitur untuk menyetel header ini menggunakan indeks byte yang disediakan, dan untuk mendapatkan rentang byte secara otomatis dari header range yang disediakan oleh pengguna.

Menyelesaikan aplikasi contoh

Pada aplikasi contoh berikut, URL utama aplikasi memuat formulir yang meminta pengguna untuk mengupload file, dan pengendali upload segera memanggil pengendali download untuk menyajikan data. Hal ini untuk menyederhanakan aplikasi contoh. Dalam praktiknya, Anda mungkin tidak akan menggunakan URL utama untuk meminta upload data, dan Anda juga tidak akan langsung menyajikan blob yang baru saja diupload.

from google.appengine.api import users
from google.appengine.ext import blobstore
from google.appengine.ext import ndb
from google.appengine.ext.webapp import blobstore_handlers
import webapp2

# This datastore model keeps track of which users uploaded which photos.
class UserPhoto(ndb.Model):
    user = ndb.StringProperty()
    blob_key = ndb.BlobKeyProperty()

class PhotoUploadFormHandler(webapp2.RequestHandler):
    def get(self):
        upload_url = blobstore.create_upload_url("/upload_photo")
        # To upload files to the blobstore, the request method must be "POST"
        # and enctype must be set to "multipart/form-data".
        self.response.out.write(
            """
<html><body>
<form action="{0}" method="POST" enctype="multipart/form-data">
  Upload File: <input type="file" name="file"><br>
  <input type="submit" name="submit" value="Submit">
</form>
</body></html>""".format(
                upload_url
            )
        )

class PhotoUploadHandler(blobstore_handlers.BlobstoreUploadHandler):
    def post(self):
        upload = self.get_uploads()[0]
        user_photo = UserPhoto(
            user=users.get_current_user().user_id(), blob_key=upload.key()
        )
        user_photo.put()

        self.redirect("/view_photo/%s" % upload.key())

class ViewPhotoHandler(blobstore_handlers.BlobstoreDownloadHandler):
    def get(self, photo_key):
        if not blobstore.get(photo_key):
            self.error(404)
        else:
            self.send_blob(photo_key)

app = webapp2.WSGIApplication(
    [
        ("/", PhotoUploadFormHandler),
        ("/upload_photo", PhotoUploadHandler),
        ("/view_photo/([^/]+)?", ViewPhotoHandler),
    ],
    debug=True,
)

Menggunakan layanan Gambar dengan Blobstore

Layanan Gambar dapat menggunakan nilai Blobstore sebagai sumber transformasi. Ukuran gambar sumber bisa sebesar ukuran maksimum untuk nilai Blobstore. Layanan Gambar tetap menampilkan gambar yang ditransformasi ke aplikasi, sehingga ukuran gambar yang ditransformasi harus lebih kecil dari 32 megabyte. Hal ini berguna untuk membuat gambar thumbnail foto besar yang diupload oleh pengguna.

Untuk mengetahui informasi tentang penggunaan layanan Gambar dengan nilai Blobstore, lihat dokumentasi Layanan Gambar.

Menggunakan Blobstore API dengan Google Cloud Storage

Anda dapat menggunakan Blobstore API untuk menyimpan blob di Cloud Storage, bukan menyimpannya di Blobstore. Anda perlu menyiapkan bucket seperti yang dijelaskan dalam dokumentasi Google Cloud Storage dan menentukan bucket serta nama file di parameter gs_bucket_name blobstore.blobstore.create_upload_url. Di pengendali upload, Anda perlu memproses metadata FileInfo yang ditampilkan dan secara eksplisit menyimpan nama file Google Cloud Storage yang diperlukan untuk mengambil blob nanti.

Anda juga dapat menyajikan objek Cloud Storage menggunakan Blobstore API. Cuplikan kode berikut menunjukkan cara melakukannya:

# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""A sample app that operates on GCS files with blobstore API."""

import cloudstorage
from google.appengine.api import app_identity
from google.appengine.ext import blobstore
from google.appengine.ext.webapp import blobstore_handlers
import webapp2

# This handler creates a file in Cloud Storage using the cloudstorage
# client library and then reads the data back using the Blobstore API.
class CreateAndReadFileHandler(webapp2.RequestHandler):
    def get(self):
        # Get the default Cloud Storage Bucket name and create a file name for
        # the object in Cloud Storage.
        bucket = app_identity.get_default_gcs_bucket_name()

        # Cloud Storage file names are in the format /bucket/object.
        filename = "/{}/blobstore_demo".format(bucket)

        # Create a file in Google Cloud Storage and write something to it.
        with cloudstorage.open(filename, "w") as filehandle:
            filehandle.write("abcde\n")

        # In order to read the contents of the file using the Blobstore API,
        # you must create a blob_key from the Cloud Storage file name.
        # Blobstore expects the filename to be in the format of:
        # /gs/bucket/object
        blobstore_filename = "/gs{}".format(filename)
        blob_key = blobstore.create_gs_key(blobstore_filename)

        # Read the file's contents using the Blobstore API.
        # The last two parameters specify the start and end index of bytes we
        # want to read.
        data = blobstore.fetch_data(blob_key, 0, 6)

        # Write the contents to the response.
        self.response.headers["Content-Type"] = "text/plain"
        self.response.write(data)

        # Delete the file from Google Cloud Storage using the blob_key.
        blobstore.delete(blob_key)

# This handler creates a file in Cloud Storage using the cloudstorage
# client library and then serves the file back using the Blobstore API.
class CreateAndServeFileHandler(blobstore_handlers.BlobstoreDownloadHandler):
    def get(self):
        # Get the default Cloud Storage Bucket name and create a file name for
        # the object in Cloud Storage.
        bucket = app_identity.get_default_gcs_bucket_name()

        # Cloud Storage file names are in the format /bucket/object.
        filename = "/{}/blobstore_serving_demo".format(bucket)

        # Create a file in Google Cloud Storage and write something to it.
        with cloudstorage.open(filename, "w") as filehandle:
            filehandle.write("abcde\n")

        # In order to read the contents of the file using the Blobstore API,
        # you must create a blob_key from the Cloud Storage file name.
        # Blobstore expects the filename to be in the format of:
        # /gs/bucket/object
        blobstore_filename = "/gs{}".format(filename)
        blob_key = blobstore.create_gs_key(blobstore_filename)

        # BlobstoreDownloadHandler serves the file from Google Cloud Storage to
        # your computer using blob_key.
        self.send_blob(blob_key)

app = webapp2.WSGIApplication(
    [
        ("/", CreateAndReadFileHandler),
        ("/blobstore/read", CreateAndReadFileHandler),
        ("/blobstore/serve", CreateAndServeFileHandler),
    ],
    debug=True,
)

Menggunakan BlobReader

Aplikasi dapat membaca data dari nilai Blobstore menggunakan antarmuka yang mirip dengan objek file Python. Antarmuka ini dapat mulai membaca nilai pada posisi byte apa pun, dan menggunakan beberapa panggilan layanan serta buffering, sehingga aplikasi dapat mengakses ukuran penuh nilai meskipun ada batasan ukuran respons panggilan layanan tunggal.

Class BlobReader dapat menggunakan salah satu dari tiga nilai sebagai argumen untuk konstruktornya:

Objek ini mengimplementasikan metode file yang sudah dikenal untuk membaca nilai. Aplikasi tidak dapat mengubah nilai Blobstore; metode file untuk menulis tidak diterapkan.

# Instantiate a BlobReader for a given Blobstore blob_key.
blob_reader = blobstore.BlobReader(blob_key)

# Instantiate a BlobReader for a given Blobstore blob_key, setting the
# buffer size to 1 MB.
blob_reader = blobstore.BlobReader(blob_key, buffer_size=1048576)

# Instantiate a BlobReader for a given Blobstore blob_key, setting the
# initial read position.
blob_reader = blobstore.BlobReader(blob_key, position=0)

# Read the entire value into memory. This may take a while depending
# on the size of the value and the size of the read buffer, and is not
# recommended for large values.
blob_reader_data = blob_reader.read()

# Write the contents to the response.
self.response.headers["Content-Type"] = "text/plain"
self.response.write(blob_reader_data)

# Set the read position back to 0, then read and write 3 bytes.
blob_reader.seek(0)
blob_reader_data = blob_reader.read(3)
self.response.write(blob_reader_data)
self.response.write("\n")

# Set the read position back to 0, then read and write one line (up to
# and including a '\n' character) at a time.
blob_reader.seek(0)
for line in blob_reader:
    self.response.write(line)

Membuat permintaan asinkron

Aplikasi dapat memanggil beberapa fungsi Blobstore yang bekerja di latar belakang. Blobstore melakukan permintaan ketika aplikasi melakukan hal-hal lain. Untuk membuat permintaan, aplikasi memanggil fungsi asinkron. Fungsi ini segera menampilkan objek RPC; objek ini mewakili permintaan. Saat memerlukan hasil permintaan, aplikasi akan memanggil metode get_result() objek RPC.

Jika layanan belum menyelesaikan permintaan saat aplikasi memanggil get_result(), metode akan menunggu hingga permintaan selesai (atau telah mencapai batas waktu, atau terjadi error). Metode ini menampilkan objek hasil, atau memunculkan pengecualian jika terjadi error saat menjalankan permintaan. Misalnya, cuplikan kode ini

upload_url = blobstore.create_upload_url('/upload')
slow_operation()
self.response.out.write("""<form action="%s" method="POST"
                           enctype="multipart/form-data">""" % upload_url)

menjadi

upload_url_rpc = blobstore.create_upload_url_async('/upload')
slow_operation()
upload_url = upload_url_rpc.get_result()
self.response.out.write("""<form action="%s" method="POST"
                           enctype="multipart/form-data">""" % upload_url)

Dalam contoh ini, aplikasi menjalankan kode slow_operation() pada saat yang sama ketika Blobstore membuat URL upload.

Kuota dan batas

Ruang yang digunakan untuk nilai Blobstore berkontribusi pada kuota Data Tersimpan (dapat ditagih). Entity info Blob di datastore diperhitungkan dalam batas terkait datastore. Perhatikan bahwa Google Cloud Storage adalah layanan bayar sesuai penggunaan; Anda akan dikenakan biaya sesuai dengan lembar harga Cloud Storage.

Untuk mengetahui informasi selengkapnya tentang kuota keamanan di seluruh sistem, lihat Kuota.

Selain kuota keamanan di seluruh sistem, batas berikut berlaku khusus untuk penggunaan Blobstore:

  • Ukuran maksimum data Blobstore yang dapat dibaca oleh aplikasi dengan satu panggilan API adalah 32 megabyte.
  • Jumlah file maksimum yang dapat diupload dalam satu format POST adalah 500.