webapp Blobstore Handlers

webapp inclui classes de processadores de pedidos para trabalhar com a API Blobstore. BlobstoreUploadHandler fornece lógica para analisar o pedido de carregamento transmitido através do Blobstore em registos BlobInfo para processamento adicional. BlobstoreDownloadHandler facilita a publicação de valores do Blobstore a partir de qualquer caminho.

BlobstoreUploadHandler

Os valores são adicionados ao Blobstore através de carregamentos de ficheiros publicados pelos utilizadores ou administradores da app. A app publica um formulário Web com um campo de carregamento de ficheiros e uma ação de formulário que direciona o carregamento para o Blobstore. A app recebe o URL de ação do formulário chamando uma função (create_upload_url()), transmitindo-lhe o URL de um controlador de apps que é chamado quando os utilizadores carregam ficheiros. Uma aplicação Web pode usar uma subclasse da classe BlobstoreUploadHandler como o controlador para este URL.

O método get_uploads() devolve uma lista de objetos BlobInfo, um para cada ficheiro carregado no pedido. Cada objeto contém a chave do Blobstore para o valor carregado, bem como metadados, como o nome do ficheiro e o tamanho. Cada ficheiro carregado também tem uma entidade correspondente no arquivo de dados com estas informações, para que possa obter o objeto BlobInfo mais tarde, dado uma chave de blob, ou executar uma consulta do arquivo de dados nos campos de metadados. O controlador de carregamento analisa estas informações diretamente a partir dos dados do pedido e não do repositório de dados.

Por predefinição, get_uploads() devolve objetos BlobInfo para todos os ficheiros carregados no pedido. O método também aceita um argumento field_name para obter apenas o ficheiro (ou os ficheiros) de um determinado campo de carregamento de ficheiros. O valor de retorno é sempre uma lista, possivelmente uma lista vazia.

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 o BlobstoreUploadHandler com o Google Cloud Storage

Se usar este controlador de carregamento com o Cloud Storage, tem de obter e armazenar o nome do ficheiro do objeto do Cloud Storage completo, uma vez que é necessário para obter novamente o ficheiro do Cloud Storage. Use a função get_file_infos, que devolve uma lista de registos FileInfo correspondentes a cada carregamento. O nome completo do objeto do Cloud Storage, o tipo de conteúdo, a hora de criação e outros dados estão disponíveis no FileInfo. (Consulte o link para ver os detalhes completos.)

BlobstoreDownloadHandler

Para publicar um valor do Blobstore, a aplicação define o cabeçalho X-AppEngine-BlobKey para o valor de uma chave do Blobstore, sob a forma de string. Quando o App Engine vê este cabeçalho na resposta, publica o valor do blob como o corpo da resposta. A classe do controlador da app Web BlobstoreDownloadHandler facilita a definição deste valor na resposta.

O método send_blob() usa um objeto BlobKey, uma chave de string ou um BlobInfo como argumento blob_key_or_info e define os dados de resposta para que o valor do blob seja apresentado ao utilizador. O método recebe um argumento content_type opcional que substitui o tipo de conteúdo MIME do valor blob armazenado. Por predefinição, o blob é publicado com o tipo de conteúdo definido pelo cliente que o carregou, um tipo de conteúdo derivado do nome do ficheiro ou um tipo genérico se não estiverem disponíveis outras informações de tipo.

O método send_blob() aceita um argumento save_as que determina se os dados do blob são enviados como dados de resposta não processados ou como um anexo MIME com um nome de ficheiro. Se o argumento for uma string, o BLOB é enviado como anexo e o valor da string é usado como nome do ficheiro. Se True e blob_key_or_info for um objeto BlobInfo, é usado o nome de ficheiro do objeto. Por predefinição, os dados de blob são enviados como o corpo da resposta e não como um anexo 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)

O Blobstore suporta o envio apenas de parte de um valor em vez do valor completo, descrito como um intervalo de índices de bytes. Pode fornecer um intervalo de índice de bytes ao método send_blob() de BlobstoreDownloadHandler de duas formas. A primeira é especificar o intervalo como os argumentos start e end:

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

Por predefinição, o BlobstoreDownloadHandler respeita o cabeçalho range no pedido. Se quiser bloquear a utilização do cabeçalho de intervalo original, forneça o 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)

O valor do cabeçalho range é um intervalo de bytes HTTP padrão. O BlobstoreDownloadHandler usa webob.byterange para analisar este valor do cabeçalho.

Aplicação de exemplo completa

Na seguinte aplicação de exemplo, o URL principal da aplicação carrega o formulário que pede ao utilizador um ficheiro para carregar, e o controlador de carregamento chama imediatamente o controlador de transferência para publicar os dados. Isto destina-se a simplificar a aplicação de exemplo. Na prática, provavelmente, não usaria o URL principal para pedir dados de carregamento, nem publicaria imediatamente um blob que acabou de carregar.

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)