Información general sobre la API Blobstore para servicios agrupados antiguos

La API Blobstore permite que tu aplicación sirva objetos de datos, llamados blobs,que son mucho más grandes que el tamaño permitido para los objetos del servicio Datastore. Los blobs resultan útiles para mostrar archivos de gran tamaño, como archivos de vídeo o de imagen, y para permitir a los usuarios subir archivos de datos grandes. Los blobs se crean al subir un archivo mediante una solicitud HTTP. Normalmente, tus aplicaciones lo harán mostrando un formulario con un campo de subida de archivos al usuario. Cuando se envía el formulario, Blobstore crea un blob a partir del contenido del archivo y devuelve una referencia opaca al blob, llamada clave de blob,que puedes usar más adelante para servir el blob. La aplicación puede proporcionar el valor completo del blob en respuesta a una solicitud de un usuario o leer el valor directamente mediante una interfaz de tipo archivo de streaming.

Introducción al almacén de blob

Google App Engine incluye el servicio Blobstore, que permite a las aplicaciones servir objetos de datos limitados únicamente por la cantidad de datos que se pueden subir o descargar a través de una sola conexión HTTP. Estos objetos se denominan valores de Blobstore o blobs. Los valores del almacén de blob se muestran como respuestas de los controladores de solicitudes y se crean como archivos subidos a través de formularios web. Las aplicaciones no crean datos de blob directamente, sino de forma indirecta, mediante un formulario web enviado u otra solicitud HTTP POST. Los valores de Blobstore se pueden proporcionar al usuario o la aplicación puede acceder a ellos en un flujo similar a un archivo mediante la API Blobstore.

Para pedir a un usuario que suba un valor de Blobstore, tu aplicación muestra un formulario web con un campo de subida de archivos. La aplicación genera la URL de acción del formulario llamando a la API Blobstore. El navegador del usuario sube el archivo directamente a Blobstore a través de la URL generada. A continuación, Blobstore almacena el blob, reescribe la solicitud para que contenga la clave del blob y la envía a una ruta de tu aplicación. Un controlador de solicitudes en esa ruta de tu aplicación puede realizar un procesamiento adicional del formulario.

Para servir un blob, tu aplicación define un encabezado en la respuesta saliente y App Engine sustituye la respuesta por el valor del blob.

Los blobs no se pueden modificar una vez creados, pero sí se pueden eliminar. Cada blob tiene un registro de información de blob correspondiente, almacenado en Datastore, que proporciona detalles sobre el blob, como la hora de creación y el tipo de contenido. Puedes utilizar la clave de blob para extraer registros de información del blob y realizar una consulta sobre sus propiedades.

Uso del almacén de blob

Las aplicaciones pueden usar Blobstore para aceptar archivos grandes que los usuarios suban y para ofrecer esos archivos. Los archivos se denominan blobs una vez que se han subido. Las aplicaciones no acceden a los blobs directamente por el nombre de archivo. En su lugar, las aplicaciones hacen referencia a los blobs mediante el tipo appengine.BlobKey.

El usuario crea un blob cuando envía un formulario HTML que incluye uno o varios campos de entrada de archivos. Tu aplicación define blobstore.UploadURL como destino (acción) de este formulario, lo que pasa a la función una ruta de URL de un controlador de tu aplicación. Cuando el usuario envía el formulario, el navegador del usuario sube los archivos especificados directamente a Blobstore. Blobstore reescribe la solicitud del usuario y almacena los datos del archivo subido, sustituyéndolos por una o varias claves de blob correspondientes. A continuación, envía la solicitud reescrita al controlador en la ruta de URL que has proporcionado a blobstore.UploadURL. Este controlador puede llevar a cabo procesos adicionales de acuerdo con la clave de blob. Por último, el controlador debe devolver una respuesta de redireccionamiento con solo la cabecera (301, 302 o 303); normalmente, un navegador redirecciona a otra página que indica el estado de la subida del blob.

La aplicación puede leer partes de un valor de Blobstore mediante un blobstore.Reader.

Subir un blob

Para crear y subir un blob, sigue este procedimiento:

1. Crear una URL de subida

Llama a blobstore.UploadURL para crear una URL de subida del formulario que rellenará el usuario. Pasa la ruta de la aplicación que se cargará cuando se complete el POST del formulario.

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

2. Crear un formulario de subida

El formulario debe incluir un campo de subida de archivos y el valor de enctype del formulario debe ser multipart/form-data. Cuando el usuario envía el formulario, la API Blobstore gestiona el POST, que crea el blob. La API también crea un registro de información del blob, lo almacena en el almacén de datos y envía la solicitud reescrita a tu aplicación en la ruta indicada como clave de blob.

	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>
`

Debe servir la página del formulario con un Content-Type de text/html; charset=utf-8. De lo contrario, los nombres de archivo con caracteres no ASCII se interpretarán de forma incorrecta.
Este es el tipo de contenido predeterminado en Go, pero debe recordar hacerlo si define su propio Content-Type.

No puedes usar un balanceador de carga de aplicación externo global con un NEG sin servidor para gestionar las solicitudes de subida enviadas a la URL /_ah/upload/ devuelta por la llamada blobstore.create_upload_url. En su lugar, debe dirigir esas solicitudes de subida directamente al servicio de App Engine. Para ello, puedes usar el dominio appspot.com o un dominio personalizado asignado directamente al servicio de App Engine.

3. Implementar un controlador de subida

En este controlador, puede almacenar la clave del blob con el resto del modelo de datos de su aplicación. Se puede seguir accediendo a la clave de blob desde la entidad de información del blob en el almacén de datos. Ten en cuenta que, una vez que el usuario envía el formulario y se invoca tu controlador, el blob ya está guardado y su información se ha añadido al almacén de datos. Si tu aplicación no quiere conservar el blob, debes eliminarlo inmediatamente para evitar que se quede huérfano:

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)

Cuando Blobstore reescribe la solicitud del usuario, se vacían los cuerpos de las partes MIME de los archivos subidos y se añade la clave de blob como encabezado de parte MIME. Se conservarán todos los demás campos y partes del formulario y se transmitirán al controlador de subida. Si no especifica un tipo de contenido, Blobstore intentará deducirlo a partir de la extensión del archivo. Si no se puede determinar el tipo de contenido, se asigna el tipo de contenido application/octet-stream al blob recién creado.

Servir un blob

Para servir blobs, debes incluir un controlador de descarga de blobs como ruta en tu aplicación. Este controlador debe transferir la clave del blob deseado a blobstore.Send. En este ejemplo, la clave de blob se transfiere al controlador de descargas como argumento de URL r.FormValue("blobKey"). En la práctica, el controlador de descarga puede obtener la clave de blob mediante cualquier método que selecciones, ya sea cualquier otro método o una acción de usuario.

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

Los blobs se pueden servir desde cualquier URL de aplicación. Para servir un blob en tu aplicación, debes incluir un encabezado especial en la respuesta que contenga la clave del blob. App Engine sustituye el cuerpo de la respuesta por el contenido del blob.

Intervalos de bytes de blobs

El almacén de blob admite la publicación de parte de un valor grande en lugar del valor completo en respuesta a una solicitud. Para servir un valor parcial, incluye el encabezado X-AppEngine-BlobRange en la respuesta saliente. Su valor es un intervalo de bytes HTTP estándar. La numeración de bytes se basa en cero. Un X-AppEngine-BlobRange en blanco indica a la API que ignore el encabezado de intervalo y que proporcione el blob completo. Entre los ejemplos de intervalos se incluye:

  • 0-499 sirve los primeros 500 bytes del valor (bytes del 0 al 499, ambos incluidos).
  • 500-999 sirve 500 bytes a partir del byte 501.
  • 500- sirve todos los bytes a partir del byte 501 hasta el final del valor.
  • -500 sirve los últimos 500 bytes del valor.

Si el intervalo de bytes es válido para el valor de Blobstore, Blobstore envía el código de estado 206 Partial Content y el intervalo de bytes solicitado al cliente. Si el intervalo no es válido para el valor, Blobstore envía 416 Requested Range Not Satisfiable.

Blobstore no admite varios intervalos de bytes en una sola solicitud (por ejemplo, 100-199,200-299), tanto si se solapan como si no.

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 que suba un archivo, 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 utilizarías la URL principal para solicitar los datos de subida ni tampoco publicarías de forma inmediata un blob que acabaras de subir.


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

Usar la API Blobstore con Google Cloud Storage

Puedes usar la API Blobstore para almacenar blobs en Cloud Storage en lugar de en Blobstore. Debes configurar un segmento tal como se describe en la documentación de Google Cloud Storage y especificar el segmento y el nombre de archivo en el UploadURLOptions que proporciones a la función UploadURL. En el controlador de subida, debes procesar el mapa devuelto de los blobs devueltos y almacenar explícitamente el nombre de archivo de Google Cloud Storage necesario para recuperar el blob más adelante.

También puedes servir objetos de Cloud Storage mediante la API Blobstore. Para servir un objeto de Cloud Storage, usa BlobKeyForFile para generar el blobkey necesario, tal como se describe en Servir un blob.

Cuotas y límites

El espacio usado para los valores de Blobstore se incluye en la cuota de datos almacenados (facturables). Las entidades de información de blobs del almacén de datos se tienen en cuenta para los límites relacionados con el almacén de datos. Ten en cuenta que Google Cloud Storage es un servicio de pago por uso. Se te cobrará según la hoja de precios de Cloud Storage.

Para obtener más información sobre las cuotas de seguridad de todo el sistema, consulta Cuotas.

Además de las cuotas de seguridad de todo el sistema, se aplican los siguientes límites específicamente al uso del almacén de blobs:

  • El tamaño máximo de los datos de Blobstore que puede leer la aplicación con una llamada a la API es de 32 megabytes.
  • El número máximo de archivos que se pueden subir en una sola publicación de formulario es de 500.
Para obtener información sobre cómo evitar estos límites de tamaño, consulta la documentación de la función Send.