Descripción general de la API de Blobstore Python

Nota: Considera usar Google Cloud Storage en lugar de Blobstore para almacenar datos BLOB.

La API de Blobstore permite que tu aplicación entregue objetos de datos, llamados blobs, que son mucho más grandes que el tamaño permitido para los objetos en el servicio de Datastore. Los blobs son útiles para entregar archivos grandes, como archivos de video o imágenes, y para que los usuarios suban archivos de datos grandes. Los blobs se crean subiendo un archivo a través de una solicitud HTTP. Por lo general, para hacerlo tus aplicaciones presentarán un formulario con un campo de subida de archivos para el usuario. Cuando se envía el formulario, Blobstore crea un blob a partir del contenido del archivo y muestra una referencia opaca al blob, llamada clave de blob, que luego puede usar para entregar el blob. La aplicación puede entregar el valor completo del blob en respuesta a una solicitud del usuario, o puede leer el valor directamente mediante una interfaz similar a un archivo de transmisión.

Presentación de Blobstore

Google App Engine incluye el servicio Blobstore, que permite a las aplicaciones entregar objetos de datos limitados solo por la cantidad de datos que se pueden subir o descargar a través de una única conexión HTTP. Estos objetos se denominan valores de Blobstore, o blobs. Los valores de Blobstore se entregan como respuestas a la solicitud de los controladores y se crean como subidas a través de formularios web. Las aplicaciones no crean datos de blob directamente; en cambio, los blobs se crean indirectamente mediante un formulario web enviado o una solicitud POST de HTTP. Los valores de Blobstore se pueden entregar al usuario, o la aplicación puede acceder a ellos en una secuencia similar a un archivo, con la API de Blobstore.

Para solicitar a un usuario que suba un valor de Blobstore, tu aplicación presenta un formulario web con un campo de subida de archivos. La aplicación genera la URL de acción del formulario llamando a la API de Blobstore. El navegador del usuario sube el archivo directamente en Blobstore a través de la URL generada. Luego, Blobstore almacena el blob, vuelve a escribir la solicitud para que contenga la clave del blob y la pasa a una ruta en tu aplicación. Un controlador de solicitudes en esa ruta de tu aplicación puede realizar un procesamiento de formulario adicional.

Para entregar un blob, tu aplicación establece un encabezado en la respuesta saliente y App Engine reemplaza la respuesta con el valor de blob.

Los blobs no se pueden modificar después de que se crean, pero sí borrar. Cada blob tiene un registro de información de blob correspondiente, guardado en el almacén de datos, que proporciona detalles sobre el blob, como su hora de creación y tipo de contenido. Puedes usar la clave de BLOB para buscar registros de información de BLOB y consultar sus propiedades.

Una aplicación puede leer un valor de Blobstore de a una parte a la vez con una llamada a la API. El tamaño de la parte puede tener hasta el máximo de un valor de muestra de API. Esto es un poco menos de 32 megabytes, representado en Python por la constante google.appengine.ext.blobstore.MAX_BLOB_FETCH_SIZE. Una aplicación no puede crear ni modificar los valores de Blobstore, excepto a través de archivos que sube el usuario.

Usar Blobstore

Las aplicaciones pueden usar el Blobstore para aceptar archivos grandes como cargas de los usuarios y entregarlos. Los archivos se llaman BLOBS una vez que se suben. Las aplicaciones no pueden acceder a los BLOB de manera directa. En cambio, las aplicaciones funcionan con BLOBS a través de entidades de información de BLOB (que están representadas por la clase BlobInfo) en el almacén de datos.

El usuario crea un blob cuando envía un formulario HTML que incluye uno o más campos de entrada de archivos. Tu aplicación llama a create_upload_url() para obtener el destino (acción) de este formulario, y pasa a la función una ruta de URL de un controlador en tu aplicación. Cuando el usuario envía el formulario, el navegador del usuario sube los archivos especificados directamente en Blobstore. Blobstore reescribe la solicitud del usuario y almacena los datos del archivo subido, reemplazando los datos del archivo subido con una o más claves de blob correspondientes; luego pasa la solicitud reescrita al controlador en la ruta de URL que proporcionaste a create_upload_url(). Este controlador puede realizar procesamientos adicionales basados en la clave de blob.

La aplicación puede leer partes de un valor de Blobstore con una interfaz de transmisión similar a un archivo. Consulta la clase BlobReader.

Sube un BLOB

Para crear y subir un blob, sigue este procedimiento:

1. Crea una URL de subida

Llama a blobstore.create_upload_url() a fin de crear una URL de subida para el formulario que llenará el usuario, pasando la ruta de la aplicación de subida cuando se complete el POST del formulario.

upload_url = blobstore.create_upload_url('/upload_photo')

Hay una versión asíncrona, create_upload_url_async(). Permite que tu código de aplicación continúe ejecutándose mientras Blobstore genera la URL de subida.

2. Crea un formulario de subida

El formulario debe incluir un campo de subida de archivos, y el enctype del formulario debe establecerse en multipart/form-data. Cuando el usuario envía el formulario, el POST es controlado por la API de Blobstore, que crea un blob. La API también crea un registro de información para el blob, almacena el registro en el almacén de datos y pasa la solicitud reescrita a tu aplicación en la ruta dada como una clave de 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 un controlador de subida

En este controlador, puedes almacenar la clave de blob con el resto de tu modelo de datos de la aplicación. La clave de blob en sí permanece accesible desde la entidad de información de blob en el almacén de datos. Ten en cuenta que después de que el usuario envíe el formulario y se llame a tu controlador, el blob ya estará guardado y la información agregada al almacén de datos. Si tu aplicación no quiere mantener el blob, debe borrarlo de inmediato para evitar que quede huérfano:

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

El marco de trabajo de aplicaciones web proporciona la clase de controlador de subida blobstore_handlers.BlobstoreUploadHandler para ayudarte a analizar los datos del formulario. Si deseas obtener más información, consulta la referencia de BlobstoreUploadHandler.

Cuando Blobstore reescribe la solicitud del usuario, las partes MIME de los archivos subidos tienen sus cuerpos vacíos y la clave de blob se agrega como parte del encabezado MIME. Todos los demás campos de formulario y las partes se conservan y se pasan al controlador de subida. Si no especificas un tipo de contenido, Blobstore intentará inferirlo de la extensión del archivo. Si no se puede determinar ningún tipo de contenido, al blob recién creado se le asigna el tipo de contenido application/octet-stream.

Entrega un BLOB

Para entregar blobs, debes incluir un controlador de descarga de blob como una ruta en tu aplicación. La aplicación entrega un blob estableciendo un encabezado en la respuesta saliente. El ejemplo siguiente utiliza el marco de trabajo de la aplicación webapp. Cuando usas webapp, el controlador debe pasar la clave de blob para el blob deseado a self.send_blob(). En este ejemplo, la clave de blob se pasa al controlador de descarga como parte de la URL. En la práctica, el controlador de descarga puede obtener la clave de blob por cualquier medio que elijas, como a través de otro método o acción del usuario.

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)

El marco de trabajo de aplicaciones web proporciona la clase de controlador de descarga blobstore_handlers.BlobstoreDownloadHandler a fin de ayudarte a analizar los datos del formulario. Si deseas obtener más información, consulta la referencia de BlobstoreDownloadHandler.

Los blobs pueden entregarse desde cualquier URL de aplicación. Para entregar un blob en tu aplicación, coloca un encabezado especial en la respuesta que contiene la clave de blob. App Engine reemplaza el cuerpo de la respuesta con el contenido del blob.

Rangos de bytes de blob

Blobstore admite el servicio de parte de un valor grande en lugar del valor total en respuesta a una solicitud. Para entregar un valor parcial, incluye el encabezado X-AppEngine-BlobRange en la respuesta saliente. Su valor es un rango de byte HTTP estándar. La numeración de bytes está basada en cero. Un X-AppEngine-BlobRange en blanco indica a la API que ignore el encabezado del rango y entregue el blob completo. Los rangos de ejemplo incluyen lo siguiente:

  • 0-499 entrega los primeros 500 bytes del valor (bytes 0 a 499, incluido).
  • 500-999 entrega 500 bytes comenzando con el byte 501.
  • 500- entrega todos los bytes que comienzan con el byte 501 hasta final del valor.
  • -500 entrega los últimos 500 bytes del valor.

Si el rango de byte es válido para el valor de Blobstore, este envía un código de estado 206 Partial Content y un rango de byte solicitado al cliente. Si el rango no es válido para el valor, Blobstore envía 416 Requested Range Not Satisfiable.

Blobstore no admite varios rangos de bytes en una sola solicitud (por ejemplo, 100-199,200-299), ya sea que se superpongan o no.

La clase webapp.blobstore_handlers.BlobstoreDownloadHandler incluye funciones para configurar este encabezado con los índices de byte proporcionados, y para derivar el rango de bytes automáticamente de un encabezado range proporcionado por el usuario.

Aplicación de muestra completa

En la aplicación de muestra siguiente, la URL principal de la aplicación carga el formulario que solicita al usuario un archivo que se debe subir. El controlador de subida llama de inmediato al controlador de descarga para entregar los datos. Esto sirve para simplificar la aplicación de muestra. En la práctica, probablemente no usarías la URL principal para solicitar datos de subida, ni tampoco entregarías de inmediato un blob que acababas de subir.

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)

Usar el servicio de imágenes con Blobstore

Con el servicio de imágenes, se puede usar un valor de Blobstore como la fuente de una transformación. La imagen fuente puede ser tan grande como el tamaño máximo de un valor de Blobstore. El servicio de imágenes aún muestra la imagen transformada a la aplicación, por lo que esta imagen debe ser menor de 32 megabytes. Esto es útil para crear imágenes en miniatura de fotografías grandes subidas por los usuarios.

Para obtener información sobre el uso de servicios de imagen con valores Blobstore, consulta la documentación sobre el servicio de imágenes.

Usar la API de Blobstore con Google Cloud Storage

Puedes usar la API de Blobstore para almacenar blobs en Cloud Storage en lugar de almacenarlos en Blobstore. Debes configurar un depósito como se describe en la documentación de Google Cloud Storage y especificar el depósito y el nombre de archivo en el parámetro blobstore.blobstore.create_upload_url gs_bucket_name. En tu controlador de subida, debes procesar los metadatos de FileInfo que muestran y almacenan explícitamente el nombre de archivo de Google Cloud Storage necesario para recuperar el blob más tarde.

También puedes entregar objetos de Cloud Storage con la API de Blobstore. Los fragmentos de código siguientes muestran cómo hacerlo:

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

Usar BlobReader

Una aplicación puede leer datos de los valores de Blobstore con una interfaz similar a un objeto file de Python. Esta interfaz puede comenzar a leer un valor en cualquier posición de byte y usa varias llamadas de servicio y almacenamiento en búfer, por lo que una aplicación puede acceder al tamaño completo del valor a pesar del límite en el tamaño de una única respuesta de llamada de servicio.

La clase BlobReader puede tomar uno de los tres valores como un argumento para su constructor:

El objeto implementa los métodos de archivo familiares para leer el valor. La aplicación no puede modificar el valor de Blobstore. Los métodos de archivo para escribir no están implementados.

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

Hacer peticiones asíncronas

Una aplicación puede llamar a algunas funciones de Blobstore que funcionan en segundo plano. Blobstore lleva a cabo la solicitud, mientras que la aplicación realiza otras acciones. Para llevar a cabo la solicitud, la aplicación llama a una función asíncrona. La función muestra inmediatamente un objeto RPC; este objeto representa la solicitud. Cuando la aplicación necesita el resultado de la solicitud, llama al método get_result() del objeto RPC.

Si el servicio no ha completado la solicitud cuando la aplicación llama a get_result(), el método espera hasta que se complete la solicitud (o hasta que se haya alcanzado la fecha límite o se produzca un error). El método muestra el objeto de resultado o genera una excepción si se produjo un error cuando se realizó la solicitud. Por ejemplo, este fragmento de código

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

se convierte en

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)

En este ejemplo, la aplicación ejecuta el código slow_operation() al mismo tiempo que Blobstore genera la URL de subida.

Cuotas y límites

El espacio usado por los valores de Blobstore contribuyen a la cuota Datos almacenados (facturable). Las entidades de información de blob en el almacén de datos cuentan con los límites relacionados con el almacén de datos. Ten en cuenta que Google Cloud Storage es un servicio de pago por uso; se te cobrará de acuerdo con la hoja de precios de Cloud Storage.

Para obtener más información sobre las cuotas de seguridad en todo el sistema, consulte Cuotas.

Además de las cuotas de seguridad para todo el sistema, los límites siguientes se aplican específicamente al uso de Blobstore:

  • El tamaño máximo de los datos de Blobstore que puede leer la aplicación con una llamada a la API es de 32 megabytes.
  • La cantidad máxima de archivos que pueden subirse en un solo formulario POST es de 500.
¿Te ha resultado útil esta página? Enviar comentarios:

Enviar comentarios sobre...

Entorno estándar de App Engine para Python 2