Visão geral da API Blobstore para pacotes de serviços legados

A API Blobstore permite que seu aplicativo disponibilize objetos de dados, chamados blobs, que são muito maiores do que o tamanho permitido para objetos no serviço do Datastore. Os blobs são úteis para exibir arquivos grandes, como arquivos de vídeo ou de imagem, e para permitir que os usuários façam upload de arquivos de dados grandes. Blobs são criados fazendo upload de um arquivo através de uma solicitação HTTP. Normalmente, seus aplicativos fazem isso apresentando ao usuário um formulário com um campo de upload de arquivos. Quando o formulário é enviado, o Blobstore cria um blob a partir do conteúdo do arquivo e retorna uma referência opaca ao blob, chamada de chave blob, que pode ser usada mais tarde para disponibilizar o blob. O aplicativo pode disponibilizar o valor de blob completo em resposta a uma solicitação do usuário ou pode ler o valor diretamente usando uma interface semelhante a um arquivo de streaming.

Apresentação do Blobstore

O Google App Engine inclui o serviço Blobstore. Com ele, os aplicativos podem disponibilizar objetos de dados, limitados somente pela quantidade de dados que podem ser salvos por upload ou download em uma única conexão HTTP. Esses objetos são chamados de valores do Blobstore ou blobs. Os valores do Blobstore são disponibilizados como respostas de gerenciadores de solicitações e criados como uploads por meio de formulários da Web. Os aplicativos não criam dados de blob diretamente. Na verdade, isso é feito indiretamente por um formulário da Web enviado ou outra solicitação HTTP POST. É possível disponibilizar os valores do Blobstore para o usuário ou acessá-los pelo aplicativo em um stream semelhante a um arquivo usando a API Blobstore.

Para solicitar que um usuário faça upload de um valor do Blobstore, o aplicativo exibe um formulário da Web com um campo de upload de arquivos. O aplicativo gera o URL de ação do formulário chamando a API Blobstore. O navegador do usuário faz upload do arquivo diretamente para o Blobstore por meio do URL gerado. Em seguida, o Blobstore armazena o blob, reescreve a solicitação para conter a chave blob e a transmite para um caminho no aplicativo. Um gerenciador de solicitações nesse caminho do aplicativo pode realizar um processamento extra do formulário.

Para disponibilizar um blob, o aplicativo define um cabeçalho na resposta de saída, e o App Engine substitui a resposta pelo valor do blob.

Os blobs não podem ser modificados depois que são criados, mas é possível excluí-los. Cada blob tem um registro de informações correspondente, no armazenamento de dados, que fornece detalhes sobre o blob, como a hora de criação e o tipo de conteúdo. Você pode usar a chave blob para buscar registros de informações e consultar as propriedades deles.

Como usar o Blobstore

Os aplicativos podem usar o Blobstore para aceitar arquivos grandes como uploads de usuários e disponibilizar esses arquivos. Os arquivos são chamados de blobs após o upload. Os aplicativos não acessam blobs diretamente pelo nome do arquivo. Em vez disso, os aplicativos se referem aos blobs por meio do tipo de appengine.BlobKey.

O usuário cria um blob enviando um formulário HTML que inclui um ou mais campos de entrada de arquivo. O aplicativo define blobstore.UploadURL como destino (ação) desse formulário, passando à função o caminho do URL de um gerenciador no aplicativo. Quando o usuário envia o formulário, o navegador faz o upload dos arquivos especificados diretamente para o Blobstore. O Blobstore grava novamente a solicitação do usuário e armazena os dados do arquivo enviado, substituindo essas informações por uma ou mais chaves blob correspondentes. Depois disso, o Blobstore transmite a solicitação regravada para o gerenciador no caminho de URL fornecido para blobstore.UploadURL. Esse gerenciador pode realizar um processamento adicional com base na chave blob. Por fim, o gerenciador precisa retornar uma resposta de redirecionamento somente em cabeçalho (301, 302 ou 303), geralmente um redirecionamento de navegador para outra página indicando o status do upload do blob.

O aplicativo pode ler partes de um valor do Blobstore usando um blobstore.Reader.

Como fazer upload de um blob

Para criar e fazer upload de um blob, siga este procedimento:

1. Crie um URL de upload

Chame blobstore.UploadURL e crie um URL de upload para o formulário que o usuário preencherá e transmita o caminho do aplicativo a ser carregado quando o POST do formulário for preenchido.

ctx := appengine.NewContext(r)
uploadURL, err := blobstore.UploadURL(ctx, "/upload", nil)
if err != nil {
	serveError(ctx, w, err)
	return
}

2. Criar um formulário de upload

O formulário precisa incluir um campo de upload de arquivos, e o enctype do formulário precisa estar definido como multipart/form-data. Quando o usuário envia o formulário, o POST é gerenciado pela API Blobstore, que cria o blob. A API também cria um registro de informações para o blob e o guarda no armazenamento de dados. Depois, a solicitação reescrita é encaminhada como uma chave blob para o aplicativo no caminho informado.

	var rootTemplate = template.Must(template.New("root").Parse(rootTemplateHTML))

	const rootTemplateHTML = `
<html><body>
<form action="{{.}}" method="POST" enctype="multipart/form-data">
Upload File: <input type="file" name="file"><br>
<input type="submit" name="submit" value="Submit">
</form></body></html>
`

3. Implementar o gerenciador de uploads

Nesse gerenciador, você pode armazenar a chave blob com o restante do modelo de dados do seu aplicativo. A chave blob em si permanece acessível a partir da entidade de informações do blob no armazenamento de dados. Observe que o blob já estará salvo e as informações do blob estarão no armazenamento de dados depois que o usuário tiver enviado o formulário e o gerenciador for chamado. Para evitar que o blob se torne órfão, exclua-o imediatamente se o aplicativo não quiser mantê-lo:

ctx := appengine.NewContext(r)
blobs, _, err := blobstore.ParseUpload(r)
if err != nil {
	serveError(ctx, w, err)
	return
}
file := blobs["file"]
if len(file) == 0 {
	log.Errorf(ctx, "no file uploaded")
	http.Redirect(w, r, "/", http.StatusFound)
	return
}
http.Redirect(w, r, "/serve/?blobKey="+string(file[0].BlobKey), http.StatusFound)

Quando o Blobstore reescreve a solicitação do usuário, o corpo das partes MIME dos arquivos enviados é esvaziado e a chave blob é adicionada como cabeçalho de parte MIME. Todos os outros campos e partes do formulário são preservados e passados para o gerenciador de upload. Se não for especificado um tipo de conteúdo, o Blobstore tentará deduzi-lo a partir da extensão do arquivo. Se um tipo de conteúdo não puder ser determinado, o blob recém-criado receberá o tipo de conteúdo application/octet-stream.

Como exibir um blob

Para disponibilizar blobs, você precisa incluir um gerenciador de download de blob como um caminho do aplicativo. Esse gerenciador encaminha a chave do blob pretendido para blobstore.Send. Neste exemplo, a chave blob é encaminhada para o gerenciador de download como o argumento do URL r.FormValue("blobKey"). Na prática, o gerenciador de download pode conseguir a chave blob como você quiser, como outro método ou ação do usuário.

blobstore.Send(w, appengine.BlobKey(r.FormValue("blobKey")))

Os blobs podem ser exibidos a partir de qualquer URL de aplicativo. Para exibir um blob no seu aplicativo, coloque um cabeçalho especial na resposta que contém a chave blob. O corpo da resposta é substituído pelo conteúdo do blob no App Engine.

Intervalos de bytes do blob

O Blobstore oferece suporte para a exibição de parte de um valor grande em vez do valor inteiro em resposta a uma solicitação. Para exibir um valor parcial, inclua o cabeçalho X-AppEngine-BlobRange na resposta enviada. O valor dele é um intervalo de bytes HTTP padrão. A numeração de bytes é baseada em zeros. Um X-AppEngine-BlobRange em branco instrui a API a ignorar o cabeçalho do intervalo e exibir o blob completo. Veja alguns exemplos de intervalos:

  • 0-499 disponibiliza os primeiros 500 bytes do valor (de 0 a 499, inclusive).
  • 500-999 disponibiliza 500 bytes a partir do byte número 501.
  • 500- disponibiliza todos os bytes a partir do byte número 501 até o fim do valor.
  • -500 disponibiliza os últimos 500 bytes do valor.

Se o intervalo de bytes for válido para o valor do Blobstore, esse sistema enviará um código de status 206 Partial Content e o intervalo de bytes solicitado para o cliente. Se o intervalo não for válido para o valor, o Blobstore enviará 416 Requested Range Not Satisfiable.

O Blobstore não oferece suporte para vários intervalos de bytes em uma única solicitação (por exemplo, 100-199,200-299), estando eles sobrepostos ou não.

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 veicularia imediatamente um blob que tivesse acabado de enviar.


package blobstore_example

import (
	"context"
	"html/template"
	"io"
	"net/http"

	"google.golang.org/appengine"
	"google.golang.org/appengine/blobstore"
	"google.golang.org/appengine/log"
)

func serveError(ctx context.Context, w http.ResponseWriter, err error) {
	w.WriteHeader(http.StatusInternalServerError)
	w.Header().Set("Content-Type", "text/plain")
	io.WriteString(w, "Internal Server Error")
	log.Errorf(ctx, "%v", err)
}

var rootTemplate = template.Must(template.New("root").Parse(rootTemplateHTML))

const rootTemplateHTML = `
<html><body>
<form action="{{.}}" method="POST" enctype="multipart/form-data">
Upload File: <input type="file" name="file"><br>
<input type="submit" name="submit" value="Submit">
</form></body></html>
`

func handleRoot(w http.ResponseWriter, r *http.Request) {
	ctx := appengine.NewContext(r)
	uploadURL, err := blobstore.UploadURL(ctx, "/upload", nil)
	if err != nil {
		serveError(ctx, w, err)
		return
	}
	w.Header().Set("Content-Type", "text/html")
	err = rootTemplate.Execute(w, uploadURL)
	if err != nil {
		log.Errorf(ctx, "%v", err)
	}
}

func handleServe(w http.ResponseWriter, r *http.Request) {
	blobstore.Send(w, appengine.BlobKey(r.FormValue("blobKey")))
}

func handleUpload(w http.ResponseWriter, r *http.Request) {
	ctx := appengine.NewContext(r)
	blobs, _, err := blobstore.ParseUpload(r)
	if err != nil {
		serveError(ctx, w, err)
		return
	}
	file := blobs["file"]
	if len(file) == 0 {
		log.Errorf(ctx, "no file uploaded")
		http.Redirect(w, r, "/", http.StatusFound)
		return
	}
	http.Redirect(w, r, "/serve/?blobKey="+string(file[0].BlobKey), http.StatusFound)
}

func init() {
	http.HandleFunc("/", handleRoot)
	http.HandleFunc("/serve/", handleServe)
	http.HandleFunc("/upload", handleUpload)
}

Como usar a Blobstore API com o Google Cloud Storage

Você pode usar a Blobstore API para armazenar blobs no Cloud Storage em vez de armazená-los no Blobstore. É preciso configurar um bucket conforme descrito na documentação do Google Cloud Storage e especificar o bucket e o nome do arquivo no UploadURLOptions que você forneceu para a função UploadURL. No gerenciador de upload, é preciso processar o mapa retornado de blobs retornados e armazenar explicitamente o nome do arquivo do Google Cloud Storage necessário para recuperar o blob posteriormente.

Você também pode disponibilizar objetos do Cloud Storage usando a API Blobstore. Para exibir um objeto do Cloud Storage, use BlobKeyForFile a fim de gerar o blobkey obrigatório, conforme descrito em Como exibir um blob.

Cotas e limites

O espaço usado para os valores do Blobstore contribui para a cota de Dados armazenados (faturáveis). As entidades de informações do blob no armazenamento de dados são contabilizadas dentro dos limites relacionados ao armazenamento de dados. O Google Cloud Storage é um serviço pago de acordo com o uso. Por isso, a cobrança será feita conforme a planilha de preços do Cloud Storage.

Para mais informações sobre as cotas de segurança do sistema, consulte Cotas.

Além das cotas de segurança do sistema, os seguintes limites são aplicados especificamente ao uso do Blobstore:

  • O tamanho máximo dos dados do Blobstore que podem ser lidos pelo aplicativo com uma chamada de API é de 32 MB.
  • O número máximo de arquivos que pode ser enviado em um único POST de formulário é 500.
Para informações sobre como alterar esses limites de tamanho, consulte a documentação da função Send.