Controladores de Blobstore de webapp

webapp incluye clases de controladores de solicitudes para trabajar con la API de Blobstore. BlobstoreUploadHandler proporciona lógica a fin de analizar la solicitud de carga que se pasa a través de Blobstore a los registros de BlobInfo para su procesamiento posterior. BlobstoreDownloadHandler facilita la entrega de valores de Blobstore desde cualquier ruta de acceso.

BlobstoreUploadHandler

Los valores se agregan a Blobstore a través de las cargas de archivos que publican los usuarios o administradores de la app. La app publica un formulario web con un campo de carga de archivos y una acción de formulario que dirige la carga a Blobstore. La aplicación obtiene la URL de la acción de formulario mediante una llamada a una función (create_upload_url()), en la que se pasa la URL de un controlador de la app al que, a su vez, se llama cuando los usuarios suben archivos. Una aplicación webapp puede usar una subclase de la clase BlobstoreUploadHandler como controlador de esta URL.

El método get_uploads() muestra una lista de objetos BlobInfo, uno para cada archivo subido en la solicitud. Cada objeto contiene la clave de Blobstore para el valor subido y metadatos como el nombre de archivo y el tamaño. A cada archivo también corresponde una entidad en el almacén de datos que incluye esa información, por lo que puedes obtener el objeto BlobInfo más tarde con una clave de BLOB o realizar una consulta del almacén de datos basada en los campos de metadatos. El controlador de carga analiza esta información directamente desde los datos de la solicitud, no desde el almacén de datos.

Según la configuración predeterminada, get_uploads() muestra objetos BlobInfo para todos los archivos subidos en la solicitud. El método también acepta un argumento field_name a fin de obtener solo el archivo (o los archivos) para un campo de carga del archivo dado. El valor que se muestra 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())

Usa BlobstoreUploadHandler con Google Cloud Storage

Si usas este controlador de carga con Cloud Storage, deberás obtener y guardar el nombre completo del archivo de objeto de Cloud Storage, ya que es necesario para volver a recuperar el archivo desde Cloud Storage. Usa la función get_file_infos, que muestra una lista de registros FileInfo correspondientes a cada carga. 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 (para obtener detalles completos, consulta el vínculo).

BlobstoreDownloadHandler

Para entregar un valor de Blobstore, la aplicación establece el encabezado X-AppEngine-BlobKey en el valor de una clave de Blobstore, con el formato de una string. Cuando App Engine ve este encabezado en la respuesta, entrega el valor del BLOB como cuerpo de la respuesta. La clase de controlador de webapp BlobstoreDownloadHandler facilita la configuración de este valor en la respuesta.

El método send_blob() toma un objeto BlobKey, una clave de string o un BlobInfo como el argumento blob_key_or_info y establece los datos de respuesta para que el valor de BLOB se entregue 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, se entrega el BLOB con el tipo de contenido establecido por el cliente que lo subió, un tipo de contenido derivado del nombre de archivo o un tipo genérico si no hay otro tipo de información disponible.

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 procesar o como un adjunto MIME con un nombre de archivo. Si el argumento es una string, el BLOB se envía como un adjunto y el valor de la string se usa como nombre de archivo. Si es True y blob_key_or_info es un objeto BlobInfo, se usa el nombre de archivo del objeto. De forma predeterminada, los datos del BLOB se envían como cuerpo de la respuesta y no como 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)

Blobstore admite el envío de solo una parte de un valor en lugar del valor completo, descrita como un rango de índices de bytes. Puedes proporcionar un rango de índice de bytes al método send_blob() de BlobstoreDownloadHandler de dos maneras. La primera es especificar el rango 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, el BlobstoreDownloadHandler respeta el encabezado range en la solicitud. Si deseas bloquear el uso del encabezado del rango 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 de range es un rango de bytes HTTP estándar. BlobstoreDownloadHandler usa webob.byterange para analizar este valor de encabezado.

Aplicación de muestra completa

En la siguiente aplicación de muestra, la URL principal de la aplicación carga el formulario que le pide al usuario que suba un archivo y el controlador de carga llama inmediatamente al controlador de descarga para que entregue los datos. Esta secuencia responde a la intención de simplificar la aplicación de muestra. En la práctica, probablemente no usarías la URL principal para solicitar datos de carga, ni entregarías de inmediato 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)