Controladores del almacén de blob webapp

webapp incluye clases de controlador de solicitudes para trabajar con la API Blobstore. BlobstoreUploadHandler proporciona la lógica para analizar la solicitud de subida enviada a través de Blobstore y convertirla en registros de BlobInfo para su posterior procesamiento. BlobstoreDownloadHandler facilita el servicio de valores de Blobstore desde cualquier ruta.

El controlador BlobstoreUploadHandler

Los valores se añaden a Blobstore mediante las subidas de archivos que publican los usuarios o los administradores de la aplicación. La aplicación publica un formulario web con un campo de subida de archivos y una acción de formulario que dirige la subida a Blobstore. La aplicación obtiene la URL de acción del formulario llamando a una función (create_upload_url()) y pasándole la URL de un controlador de la aplicación al que se llama cuando los usuarios suben archivos. Una aplicación web puede usar una subclase de la clase BlobstoreUploadHandler como controlador de esta URL.

El método get_uploads() devuelve una lista de objetos BlobInfo, uno por cada archivo subido en la solicitud. Cada objeto contiene la clave de Blobstore del valor subido, así como metadatos como el nombre de archivo y el tamaño. Cada archivo subido también tiene una entidad correspondiente en el almacén de datos con esta información, por lo que puedes obtener el objeto BlobInfo más adelante con una clave de blob o realizar una consulta del almacén de datos en los campos de metadatos. El controlador de subida analiza esta información directamente de los datos de la solicitud, no del almacén de datos.

De forma predeterminada, get_uploads() devuelve objetos BlobInfo de todos los archivos subidos en la solicitud. El método también acepta un argumento field_name para obtener solo el archivo (o los archivos) de un campo de subida de archivos determinado. El valor devuelto siempre es una lista, posiblemente vacía.

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

Usar BlobstoreUploadHandler con Google Cloud Storage

Si usas este controlador de subida con Cloud Storage, tendrás que obtener y almacenar el nombre de archivo de objeto de Cloud Storage completo, ya que es necesario para volver a obtener el archivo de Cloud Storage. Usa la función get_file_infos, que devuelve una lista de registros FileInfo correspondientes a cada subida. El nombre completo del objeto de Cloud Storage, el tipo de contenido, la hora de creación y otros datos están disponibles en FileInfo. (Consulta el enlace para obtener todos los detalles).

El controlador BlobstoreDownloadHandler

Para publicar un valor de Blobstore, la aplicación asigna al encabezado X-AppEngine-BlobKey el valor de una clave de Blobstore en formato de cadena. Cuando App Engine ve este encabezado en la respuesta, sirve el valor del blob como cuerpo de la respuesta. La clase de controlador de aplicaciones web BlobstoreDownloadHandler facilita la definición de este valor en la respuesta.

El método send_blob() toma un objeto BlobKey, una clave de cadena o un objeto BlobInfo como argumento blob_key_or_info y define los datos de respuesta para que se proporcione el valor del blob al usuario. El método toma un argumento content_type opcional que anula el tipo de contenido MIME del valor del blob almacenado. De forma predeterminada, el blob se ofrece con el tipo de contenido definido por el cliente que lo subió, un tipo de contenido derivado del nombre de archivo o un tipo genérico si no hay disponible otro tipo de información.

El método send_blob() acepta un argumento save_as que determina si los datos del blob se envían como datos de respuesta sin formato o como un archivo adjunto MIME con un nombre de archivo. Si el argumento es una cadena, el blob se envía como archivo adjunto y el valor de la cadena se usa como nombre de archivo. Si True y blob_key_or_info son objetos BlobInfo, se usa el nombre de archivo del objeto. De forma predeterminada, los datos de blob se envían como cuerpo de la respuesta y no como un archivo adjunto MIME.

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 almacén de blob admite el envío parcial de un valor en lugar del valor entero, descrito como un intervalo de índices de bytes. Puedes proporcionar un intervalo de índice de bytes al método send_blob() de BlobstoreDownloadHandler de dos formas. La primera es especificar el intervalo como los argumentos start y end:

            # Send the first 1,000 bytes of the value.
            self.send_blob(key, start=0, end=999)

De forma predeterminada, BlobstoreDownloadHandler respeta el encabezado range de la solicitud. Si quieres bloquear el uso del encabezado de intervalo original, proporciona el parámetro use_range=False a send_blob():

            # Send the full value of the blob and
            # block the "range" header.
            self.send_blob(key, use_range=False)

El valor del encabezado range es un intervalo de bytes HTTP estándar. BlobstoreDownloadHandler usa webob.byterange para analizar este valor de encabezado.

Aplicación de ejemplo completa

En la siguiente aplicación de ejemplo, la URL principal de la aplicación carga el formulario que pide al usuario un archivo para subir, y el controlador de subida llama inmediatamente al controlador de descarga para servir los datos. Esto se hace para simplificar la aplicación de ejemplo. En la práctica, probablemente no usarías la URL principal para solicitar datos de subida ni servirías inmediatamente un blob que acabas 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)