Gerenciadores de blobstore do webapp

O webapp inclui classes do gerenciador de solicitação para trabalhar com a Blobstore API. BlobstoreUploadHandler fornece a lógica para analisar a solicitação de upload transmitida pelo Blobstore para os registros de BlobInfo para processamento adicional. BlobstoreDownloadHandler facilita a disponibilização de valores do Blobstore a partir de qualquer caminho.

BlobstoreUploadHandler

Os valores são adicionados ao Blobstore por meio de uploads de arquivos postados por usuários ou administradores do aplicativo. O aplicativo posta um formulário da Web com um campo de upload de arquivo e uma ação de formulário que direciona o upload para o Blobstore. O aplicativo recebe o URL da ação de formulário chamando uma função (create_upload_url()), transmitindo a ela o URL de um gerenciador de aplicativos que é chamado quando os usuários fazem upload de arquivos. Um aplicativo do webapp pode usar uma subclasse da classe BlobstoreUploadHandler como o manipulador para esse URL.

O método get_uploads() retorna uma lista de objetos BlobInfo, um para cada arquivo enviado na solicitação. Cada objeto contém a chave do Blobstore para o valor carregado e metadados, como o nome do arquivo e o tamanho. Cada arquivo carregado também tem uma entidade correspondente no armazenamento de dados com essas informações. Assim, você pode buscar o objeto BlobInfo mais tarde com uma chave do blob ou executar uma consulta de armazenamento de dados nos campos de metadados. O gerenciador de uploads analisa essas informações diretamente nos dados da solicitação, não no armazenamento de dados.

Por padrão, get_uploads() retorna objetos do BlobInfo para todos os arquivos carregados na solicitação. O método também aceita um argumento field_name para receber apenas o arquivo (ou arquivos) de um determinado campo de upload de arquivo. O valor retornado é 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())

Como usar BlobstoreUploadHandler com o Google Cloud Storage

Se você usar esse gerenciador de upload com o Cloud Storage, precisará conhecer e armazenar o nome completo do arquivo do objeto do Cloud Storage, já que isso é necessário para recuperar o arquivo novamente no Cloud Storage. Use a função get_file_infos, que retorna uma lista de registros de FileInfo correspondentes a cada upload. O nome completo do objeto do Cloud Storage, o tipo de conteúdo, o horário de criação e outros dados estão disponíveis no FileInfo. Consulte o link para detalhes completos.

BlobstoreDownloadHandler

Para publicar um valor do Blobstore, o aplicativo define o cabeçalho X-AppEngine-BlobKey como o valor de uma chave do Blobstore, na forma de string. Quando o App Engine vê esse cabeçalho na resposta, ele disponibiliza o valor do blob como o corpo da resposta. A classe de manipulador do webapp BlobstoreDownloadHandler facilita a definição desse 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 da resposta para que o valor do blob será exibido ao usuário. O método aceita um argumento content_type opcional que substitui o tipo de conteúdo MIME do valor do blob armazenado. Por padrão, o blob é disponibilizado com o tipo de conteúdo definido pelo cliente que o carregou, um tipo de conteúdo derivado do nome de arquivo ou um tipo genérico, se nenhuma outra informação do tipo estiver disponível.

O método send_blob() aceita um argumento save_as que determina se os dados do blob são enviados como dados de resposta brutos ou como um anexo MIME com um nome de arquivo. Se o argumento for uma string, o blob será enviado como um anexo e o valor da string será usado como o nome do arquivo. Se True e blob_key_or_info forem um objeto BlobInfo, será usado o nome de arquivo do objeto. Por padrão, os dados do 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 total, descrito como um intervalo de índices de bytes. Você pode fornecer um intervalo de índice de bytes para o BlobstoreDownloadHandler do método send_blob() 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 padrão, o BlobstoreDownloadHandler honra o cabeçalho range na solicitação. Se você quer bloquear o uso do cabeçalho de intervalo original, forneça o parâmetro use_range=False para 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 (em inglês). BlobstoreDownloadHandler usa webob.byterange para analisar esse valor de cabeçalho.

Aplicativo de amostra completo

No aplicativo de amostra a seguir, o URL principal do aplicativo carrega o formulário em que o usuário faz upload do arquivo e o gerenciador de upload imediatamente chama o gerenciador de download para disponibilizar os dados. O objetivo aqui é simplificar o aplicativo de amostra. Na prática, você provavelmente não usaria o URL principal para solicitar os dados para upload, nem disponibilizaria imediatamente um blob que tivesse acabado de enviar.

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)