Panoramica dell'API Blobstore per i servizi in bundle legacy

L'API Blobstore consente all'applicazione di gestire oggetti di dati, chiamati blob, molto più grandi delle dimensioni consentite per gli oggetti nel servizio Datastore. I BLOB sono utili per pubblicare file di grandi dimensioni, ad esempio file video o immagine, e per consentire agli utenti di caricare file di dati di grandi dimensioni. I BLOB vengono creati caricando un file tramite una richiesta HTTP. In genere, le applicazioni lo fanno presentando all'utente un modulo con un campo per il caricamento di file. Una volta inviato il modulo, l'archivio BLOB crea un BLOB dai contenuti del file e restituisce un riferimento opaco al BLOB, chiamato chiave BLOB,che puoi utilizzare in un secondo momento per pubblicare il blob. L'applicazione può fornire il valore del blob completo in risposta alla richiesta di un utente oppure può leggere il valore direttamente utilizzando un'interfaccia simile a un file in modalità flusso.

Introduzione al Blobstore

Google App Engine include il servizio Blobstore, che consente alle applicazioni di gestire oggetti dati limitati solo dalla quantità di dati che possono essere caricati o scaricati tramite una singola connessione HTTP. Questi oggetti sono denominati valori dell'archivio BLOB o BLOB. I valori dell'archivio BLOB vengono pubblicati come risposte dai gestori delle richieste e creati come caricamenti tramite moduli web. Le applicazioni non creano direttamente i dati dei blob, ma i blob vengono creati indirettamente, tramite un modulo web inviato o altra richiesta POST HTTP. I valori dell'archivio BLOB possono essere inviati all'utente o accessibili dall'applicazione in un flusso simile a un file utilizzando l'API Blobstore.

Per richiedere a un utente di caricare un valore Blobstore, la tua applicazione presenta un modulo web con un campo per il caricamento di file. L'applicazione genera l'URL di azione del modulo chiamando l'API Blobstore. Il browser dell'utente carica il file direttamente nel Blobstore tramite l'URL generato. L'archivio BLOB archivia quindi il blob, riscrive la richiesta in modo che contenga la chiave BLOB e la passa a un percorso nell'applicazione. Un gestore di richieste in quel percorso della tua applicazione può eseguire ulteriori elaborazioni dei moduli.

Per gestire un blob, l'applicazione imposta un'intestazione per la risposta in uscita e App Engine sostituisce la risposta con il valore del blob.

Dopo la creazione, i BLOB non possono essere modificati, ma possono essere eliminati. Ogni BLOB ha un record di informazioni sul blob corrispondente, archiviato nel datastore, che fornisce dettagli sul blob, come l'ora di creazione e il tipo di contenuti. Puoi utilizzare la chiave BLOB per recuperare i record con informazioni sul blob ed eseguire query sulle relative proprietà.

Un'applicazione può leggere un valore Blobstore una parte alla volta utilizzando una chiamata API. La dimensione della porzione può raggiungere le dimensioni massime del valore restituito da un'API. Questa dimensione è di poco inferiore a 32 megabyte, rappresentata in Python dalla costante google.appengine.ext.blobstore.MAX_BLOB_FETCH_SIZE. Un'applicazione non può creare o modificare i valori dell'archivio BLOB, se non tramite i file caricati dall'utente.

Utilizzo di Blobstore

Le applicazioni possono utilizzare l'archivio BLOB per accettare file di grandi dimensioni come caricamenti da parte degli utenti e per pubblicarli. Una volta caricati, i file vengono chiamati BLOB. Le applicazioni non accedono direttamente ai BLOB. Le applicazioni utilizzano invece i BLOB tramite entità di informazioni BLOB (rappresentate dalla classe BlobInfo) nel datastore.

L'utente crea un blob inviando un modulo HTML che include uno o più campi di input del file. L'applicazione chiama create_upload_url() per ottenere la destinazione (azione) di questo modulo, trasmettendo alla funzione il percorso dell'URL di un gestore nell'applicazione. Quando l'utente invia il modulo, il browser dell'utente carica i file specificati direttamente nell'archivio BLOB. L'archivio BLOB riscrive la richiesta dell'utente e archivia i dati del file caricato, sostituendo i dati del file caricato con una o più chiavi BLOB corrispondenti, quindi passa la richiesta riscritta al gestore nel percorso dell'URL che hai fornito a create_upload_url(). Questo gestore può eseguire ulteriori elaborazioni in base alla chiave BLOB.

L'applicazione può leggere parti di un valore dell'archivio BLOB utilizzando un'interfaccia di flusso simile a un file. Vedi Il corso BlobReader.

Caricamento di un blob

Per creare e caricare un blob, segui questa procedura:

1. Crea un URL di caricamento

Richiama blobstore.create_upload_url() per creare un URL di caricamento per il modulo che l'utente compilerà, indicando il percorso di caricamento della richiesta una volta completato POST del modulo.

upload_url = blobstore.create_upload_url("/upload_photo")

Esiste una versione asincrona, create_upload_url_async(). Consente di continuare a eseguire il codice dell'applicazione mentre Blobstore genera l'URL di caricamento.

2. Crea un modulo di caricamento

Il modulo deve includere un campo per il caricamento di file e il valore enctype del modulo deve essere impostato su multipart/form-data. Quando l'utente invia il modulo, POST viene gestito dall'API Blobstore, che crea il blob. L'API crea anche un record informativo per il blob, archivia il record nel datastore e passa la richiesta riscritta alla tua applicazione sul percorso specificato come chiave 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. Implementa il gestore del caricamento

In questo gestore, puoi archiviare la chiave BLOB con il resto del modello dei dati dell'applicazione. La chiave blob stessa rimane accessibile dall'entità delle informazioni sul blob nel datastore. Tieni presente che, dopo che l'utente ha inviato il modulo e viene chiamato il tuo gestore, il blob è già stato salvato e le informazioni del blob sono state aggiunte al datastore. Se la tua applicazione non vuole conservare il blob, devi eliminare il blob immediatamente per evitare che diventi orfano:

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())

Il webapp framework fornisce la classe di gestore del caricamento blobstore_handlers.BlobstoreUploadHandler per aiutarti ad analizzare i dati del modulo. Per ulteriori informazioni, consulta la documentazione di riferimento per BlobstoreUploadHandler.

Quando l'archivio BLOB riscrive la richiesta dell'utente, il corpo delle parti MIME dei file caricati viene svuotato e la chiave BLOB viene aggiunta come intestazione della parte MIME. Tutti gli altri campi e parti del modulo vengono conservati e passati al gestore di caricamento. Se non specifichi un tipo di contenuto, l'archivio BLOB proverà a deducerlo dall'estensione del file. Se non è possibile determinare il tipo di contenuto, al blob appena creato viene assegnato il tipo di contenuto application/octet-stream.

Pubblicazione di un blob

Per pubblicare i BLOB, devi includere un gestore del download dei BLOB come percorso nell'applicazione. L'applicazione gestisce un blob impostando un'intestazione sulla risposta in uscita. Nell'esempio seguente viene utilizzato il framework webapp. Quando utilizzi webapp, il gestore deve passare la chiave blob per il blob desiderato a self.send_blob(). In questo esempio, la chiave blob viene passata al gestore dei download come parte dell'URL. In pratica, il gestore dei download può recuperare la chiave blob con qualsiasi mezzo a tua scelta, ad esempio tramite un altro metodo o un'azione utente.

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)

Il webapp framework fornisce la classe di gestore dei download blobstore_handlers.BlobstoreDownloadHandler per aiutarti ad analizzare i dati del modulo. Per ulteriori informazioni, consulta la documentazione di riferimento per BlobstoreDownloadHandler.

I BLOB possono essere pubblicati da qualsiasi URL dell'applicazione. Per pubblicare un blob nell'applicazione, dovrai inserire un'intestazione speciale nella risposta contenente la chiave blob. App Engine sostituisce il corpo della risposta con il contenuto del blob.

Intervalli di byte BLOB

L'archivio BLOB supporta la gestione di parte di un valore grande anziché del valore completo in risposta a una richiesta. Per pubblicare un valore parziale, includi l'intestazione X-AppEngine-BlobRange nella risposta in uscita. Il suo valore è un intervallo di byte HTTP standard. La numerazione di byte è in base zero. Un valore X-AppEngine-BlobRange vuoto indica all'API di ignorare l'intestazione dell'intervallo e pubblicare il blob completo. Gli intervalli di esempio includono:

  • 0-499 pubblica i primi 500 byte del valore (byte da 0 a 499 inclusi).
  • 500-999 gestisce 500 byte a partire dal 501° byte.
  • 500- gestisce tutti i byte a partire dal 501° byte fino alla fine del valore.
  • -500 pubblica gli ultimi 500 byte del valore.

Se l'intervallo di byte è valido per il valore dell'archivio BLOB, quest'ultimo invia al client un codice di stato 206 Partial Content e l'intervallo di byte richiesto. Se l'intervallo non è valido per il valore, l'archivio BLOB invia 416 Requested Range Not Satisfiable.

L'archivio BLOB non supporta più intervalli di byte in una singola richiesta (ad esempio 100-199,200-299), indipendentemente dal fatto che si sovrappongano o meno.

La classe webapp.blobstore_handlers.BlobstoreDownloadHandler include funzionalità per impostare questa intestazione utilizzando indici di byte forniti e per ricavare automaticamente l'intervallo di byte da un'intestazione range fornita dall'utente.

Completa l'applicazione di esempio

Nella seguente applicazione di esempio, l'URL principale dell'applicazione carica il modulo che richiede all'utente un file da caricare e il gestore del caricamento chiama immediatamente il gestore dei download per pubblicare i dati. Questo serve a semplificare l'applicazione di esempio. In pratica, probabilmente non useresti l'URL principale per richiedere i dati di caricamento né pubblicheresti immediatamente un blob appena caricato.

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,
)

Utilizzo del servizio Images con Blobstore

Il servizio Images può utilizzare un valore dell'archivio BLOB come origine di una trasformazione. L'immagine di origine può raggiungere le dimensioni massime consentite per un valore di Blobstore. Il servizio Images restituisce comunque l'immagine trasformata all'applicazione, quindi l'immagine trasformata deve avere dimensioni inferiori a 32 MB. Questa funzionalità è utile per creare miniature di fotografie di grandi dimensioni caricate dagli utenti.

Per informazioni sull'utilizzo del servizio Images con i valori dell'archivio BLOB, consulta la documentazione del servizio immagini.

Utilizzo dell'API Blobstore con Google Cloud Storage

Puoi utilizzare l'API Blobstore per archiviare i BLOB in Cloud Storage anziché archiviarli in un archivio BLOB. Devi configurare un bucket come descritto nella documentazione di Google Cloud Storage e specificare il bucket e il nome file nel parametro blobstore.blobstore.create_upload_url gs_bucket_name. Nel gestore di caricamento, devi elaborare i metadati FileInfo restituiti e archiviare esplicitamente il nome file Google Cloud Storage necessario per recuperare il blob in un secondo momento.

Puoi gestire oggetti Cloud Storage anche utilizzando l'API Blobstore. I seguenti snippet di codice spiegano come eseguire questa operazione:

# 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,
)

Utilizzo di BlobReader

Un'applicazione può leggere i dati dai valori dell'archivio BLOB utilizzando un'interfaccia simile a un oggetto file Python. Questa interfaccia può iniziare a leggere un valore in qualsiasi posizione di byte e utilizza più chiamate di servizio e buffering, in modo che un'applicazione possa accedere alla dimensione completa del valore nonostante il limite di dimensioni di una singola risposta di chiamata di servizio.

La classe BlobReader può utilizzare uno dei tre valori come argomento per il suo costruttore:

L'oggetto implementa i metodi file familiari per la lettura del valore. L'applicazione non può modificare il valore dell'archivio BLOB; i metodi di scrittura del file non sono implementati.

# 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)

Esecuzione di richieste asincrone

Un'applicazione può chiamare alcune funzioni dell'archivio BLOB che funzionano in background. L'archivio BLOB esegue la richiesta mentre l'applicazione esegue altre operazioni. Per effettuare la richiesta, l'applicazione chiama una funzione asincrona. La funzione restituisce immediatamente un oggetto RPC, che rappresenta la richiesta. Quando l'applicazione ha bisogno del risultato della richiesta, chiama il metodo get_result() dell'oggetto RPC.

Se il servizio non ha completato la richiesta quando l'applicazione chiama get_result(), il metodo attende fino al completamento della richiesta (o fino al raggiungimento della scadenza o fino a un errore). Il metodo restituisce l'oggetto risultato o genera un'eccezione se si è verificato un errore durante l'esecuzione della richiesta. Ad esempio, questo snippet di codice

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

diventa

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)

In questo esempio, l'applicazione esegue il codice slow_operation() nello stesso momento in cui Blobstore genera l'URL di caricamento.

Quote e limiti

Lo spazio utilizzato per i valori dell'archivio BLOB contribuisce alla quota Dati archiviati (fatturabili). Le entità delle informazioni BLOB nel datastore vengono conteggiate ai fini dei limiti relativi al datastore. Tieni presente che Google Cloud Storage è un servizio a pagamento; ti verranno addebitati i costi previsti dal listino prezzi di Cloud Storage.

Per ulteriori informazioni sulle quote di sicurezza a livello di sistema, consulta la sezione Quote.

Oltre alle quote di sicurezza a livello di sistema, all'uso dell'archivio BLOB si applicano i seguenti limiti specifici:

  • La dimensione massima dei dati dell'archivio BLOB che possono essere letti dall'applicazione con una chiamata API è di 32 MB.
  • Il numero massimo di file che possono essere caricati in un singolo modulo POST è 500.