Descripción general de la API de Blobstore para servicios agrupados en paquetes heredados

La API de Blobstore permite que tu aplicación entregue objetos de datos, llamados blobs, que son mucho más grandes que el tamaño permitido para los objetos en el servicio de Datastore. Los BLOB son útiles en la entrega de archivos grandes, como archivos de video o de imagen, y para permitir que los usuarios suban archivos de datos grandes. Los BLOB se crean cuando subes un archivo a través de una solicitud HTTP. Por lo general, las aplicaciones realizarán esta acción mediante un formulario para el usuario con un campo de carga de archivos. Cuando se envía el formulario, Blobstore crea un BLOB a partir del contenido del archivo y muestra una referencia opaca al BLOB, llamada clave de BLOB, que luego puedes usar para entregar el BLOB. La aplicación puede entregar el valor completo del blob en respuesta a una solicitud del usuario, o puede leer el valor directamente mediante una interfaz similar a un archivo de transmisión.

Presentación de Blobstore

Google App Engine incluye el servicio Blobstore, que permite a las aplicaciones entregar objetos de datos limitados solo por la cantidad de datos que se pueden subir o descargar a través de una única conexión HTTP. Estos objetos se denominan valores de Blobstore, o blobs. Los valores de Blobstore se entregan como respuestas a la solicitud de los controladores y se crean como cargas a través de formularios web. Las aplicaciones no crean datos BLOB directamente. En cambio, los BLOB se crean de forma indirecta mediante un formulario web enviado o una solicitud HTTP POST. Los valores de Blobstore se pueden entregar al usuario, o la aplicación puede acceder a ellos en una transmisión similar a archivos, mediante la API de Blobstore.

Para solicitar a un usuario que suba un valor de Blobstore, tu aplicación presenta un formulario web con un campo de carga de archivos. La aplicación genera la URL de acción del formulario mediante una llamada a la API de Blobstore. El navegador del usuario sube el archivo directamente a Blobstore a través de la URL generada. Luego, Blobstore almacena el BLOB, reescribe la solicitud para que contenga la clave de BLOB y la pasa a una ruta de acceso en la aplicación. Un controlador de solicitudes en esa ruta de tu aplicación puede realizar un procesamiento de formulario adicional.

Para entregar un blob, tu aplicación establece un encabezado en la respuesta saliente y App Engine reemplaza la respuesta con el valor de blob.

Los blobs no se pueden modificar después de que se crean, pero sí borrar. Cada blob tiene un registro de información de blob correspondiente, guardado en el almacén de datos, que proporciona detalles sobre el blob, como su hora de creación y tipo de contenido. Puedes usar la clave de BLOB para buscar registros de información de BLOB y consultar sus propiedades.

Una aplicación puede leer un valor de Blobstore de a una parte a la vez con una llamada a la API. El tamaño de la parte puede tener hasta el máximo de un valor de muestra de API. Este es un poco menor de 32 megabytes, que se representa en Java mediante la constante com.google.appengine.api.blobstore.BlobstoreService.MAX_BLOB_FETCH_SIZE. Una aplicación no puede crear ni modificar los valores de Blobstore, excepto a través de archivos que sube el usuario.

Usa Blobstore

Las aplicaciones pueden usar el Blobstore para aceptar archivos grandes como cargas de los usuarios y entregarlos. Los archivos se llaman BLOBS una vez que se suben. Las aplicaciones no pueden acceder a los BLOB de manera directa. En cambio, las aplicaciones funcionan con BLOB a través de entidades de información de BLOB (representadas por la clase BlobInfo) en el almacén de datos.

El usuario crea un BLOB cuando envía un formulario HTML que incluye uno o más campos de entrada de archivos. La aplicación configura blobstoreService.createUploadUrl() como el destino (acción) del formulario y pasa a la función una ruta de URL de un controlador en 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, almacena los datos del archivo subido y los reemplaza por una o más claves de BLOB correspondientes. Luego, pasa la solicitud reescrita al controlador en la ruta de URL que proporcionaste a blobstoreService.createUploadUrl(). Este controlador puede realizar procesamientos adicionales según la clave de BLOB.

La aplicación puede leer partes de un valor de Blobstore con una interfaz de transmisión similar a archivos. Consulta la clase BlobstoreInputStream.

Sube un BLOB

Para crear y subir un blob, sigue este procedimiento:

1. Crea una URL de carga

Llama a blobstoreService.createUploadUrl a fin de crear una URL de carga para el formulario que completará el usuario y pasa la ruta de la aplicación que debe cargarse cuando se complete el POST del formulario.

<body>
    <form action="<%= blobstoreService.createUploadUrl("/upload") %>" method="post" enctype="multipart/form-data">
        <input type="file" name="myFile">
        <input type="submit" value="Submit">
    </form>
</body>

Ten en cuenta que así se verá el formulario de carga si se crea como un JSP.

2. Crea un formulario de carga

El formulario debe incluir un campo de carga de archivos y el enctype del formulario debe establecerse como multipart/form-data. Cuando el usuario envía el formulario, se controla el POST mediante la API de Blobstore, que crea el BLOB. También crea un registro de información para el BLOB, almacena el registro en el almacén de datos y pasa la solicitud reescrita a tu aplicación en la ruta dada como una clave de BLOB.

3. Implementa un controlador de subida

En este controlador, puedes almacenar la clave de blob con el resto de tu modelo de datos de la aplicación. La clave de blob en sí permanece accesible desde la entidad de información de blob en el almacén de datos. Ten en cuenta que después de que el usuario envíe el formulario y se llame a tu controlador, el blob ya estará guardado y la información agregada al almacén de datos. Si tu aplicación no quiere conservar el BLOB, debes borrarlo de inmediato para evitar que quede huérfano:

En el siguiente código, getUploads muestra un conjunto de BLOB que se subieron. El objeto Map es una lista que asocia los nombres de los campos de carga a los BLOB que contenían.

Map<String, List<BlobKey>> blobs = blobstoreService.getUploads(req);
List<BlobKey> blobKeys = blobs.get("myFile");

if (blobKeys == null || blobKeys.isEmpty()) {
    res.sendRedirect("/");
} else {
    res.sendRedirect("/serve?blob-key=" + blobKeys.get(0).getKeyString());
}

Cuando Blobstore reescribe la solicitud del usuario, se vacían los cuerpos de las partes MIME de los archivos subidos y se agrega la clave BLOB como el encabezado de la parte MIME. Todos los demás campos de formulario y las partes se conservan y se pasan al controlador de subida. Si no especificas un tipo de contenido, Blobstore intentará inferirlo de la extensión del archivo. Si no se puede determinar un tipo de contenido, se asigna el tipo de contenido application/octet-stream al BLOB recién creado.

Entrega un BLOB

Para entregar BLOB, debes incluir un controlador de descarga de BLOB como una ruta en tu aplicación. Este controlador debe pasar la clave de BLOB del BLOB deseado a blobstoreService.serve(blobKey, res);. En este ejemplo, la clave de BLOB se pasa al controlador de descarga como el argumento de URL (req.getParameter('blob-key')). En la práctica, el controlador de descarga puede obtener la clave de BLOB por cualquier medio que elijas, por ejemplo, a través de otro método o de otra acción del usuario.

public void doGet(HttpServletRequest req, HttpServletResponse res)
    throws IOException {
        BlobKey blobKey = new BlobKey(req.getParameter("blob-key"));
        blobstoreService.serve(blobKey, res);

Los BLOB se pueden entregar desde cualquier URL de aplicación. Para entregar un BLOB en la aplicación, debes colocar un encabezado especial en la respuesta que contiene la clave de BLOB. App Engine reemplaza el cuerpo de la respuesta por el contenido del BLOB.

Rangos de bytes de BLOB

Blobstore admite la entrega de parte de un valor grande en lugar del valor completo en respuesta a una solicitud. Para entregar un valor parcial, incluye el encabezado X-AppEngine-BlobRange en la respuesta saliente. El valor es un rango de byte HTTP estándar. La numeración de bytes está basada en cero. Un X-AppEngine-BlobRange en blanco indica a la API que ignore el encabezado del rango y entregue el BLOB completo. Los rangos de ejemplo incluyen lo siguiente:

  • 0-499 entrega los primeros 500 bytes del valor (bytes 0 a 499, inclusive).
  • 500-999 entrega 500 bytes a partir del byte 501.
  • 500- entrega todos los bytes desde el byte 501 hasta el final del valor.
  • -500 entrega los últimos 500 bytes del valor.

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

Blobstore no admite varios rangos de bytes en una sola solicitud (por ejemplo, 100-199,200-299), sin importar si se superponen o no.

Aplicación de muestra completa

En la aplicación de muestra siguiente, la URL principal de la aplicación carga el formulario que solicita al usuario un archivo que se debe subir. El controlador de subida llama de inmediato al controlador de descarga para entregar los datos. Esto sirve para simplificar la aplicación de muestra. En la práctica, es probable que no debas usar la URL principal para solicitar datos de carga ni entregar de inmediato un BLOB que acabas de subir.

// file Upload.java

import java.io.IOException;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.appengine.api.blobstore.BlobKey;
import com.google.appengine.api.blobstore.BlobstoreService;
import com.google.appengine.api.blobstore.BlobstoreServiceFactory;

public class Upload extends HttpServlet {
    private BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();

    @Override
    public void doPost(HttpServletRequest req, HttpServletResponse res)
        throws ServletException, IOException {

        Map<String, List<BlobKey>> blobs = blobstoreService.getUploads(req);
        List<BlobKey> blobKeys = blobs.get("myFile");

        if (blobKeys == null || blobKeys.isEmpty()) {
            res.sendRedirect("/");
        } else {
            res.sendRedirect("/serve?blob-key=" + blobKeys.get(0).getKeyString());
        }
    }
}

// file Serve.java

import java.io.IOException;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.appengine.api.blobstore.BlobKey;
import com.google.appengine.api.blobstore.BlobstoreService;
import com.google.appengine.api.blobstore.BlobstoreServiceFactory;

public class Serve extends HttpServlet {
    private BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();

    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse res)
        throws IOException {
            BlobKey blobKey = new BlobKey(req.getParameter("blob-key"));
            blobstoreService.serve(blobKey, res);
        }
}

// file index.jsp

<%@ page import="com.google.appengine.api.blobstore.BlobstoreServiceFactory" %>
<%@ page import="com.google.appengine.api.blobstore.BlobstoreService" %>

<%
    BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();
%>

<html>
    <head>
        <title>Upload Test</title>
    </head>
    <body>
        <form action="<%= blobstoreService.createUploadUrl("/upload") %>" method="post" enctype="multipart/form-data">
            <input type="text" name="foo">
            <input type="file" name="myFile">
            <input type="submit" value="Submit">
        </form>
    </body>
</html>

// web.xml

<?xml version="1.0" encoding="utf-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns="http://java.sun.com/xml/ns/javaee"
   xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
   http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">

  <servlet>
    <servlet-name>Upload</servlet-name>
    <servlet-class>Upload</servlet-class>
  </servlet>

  <servlet>
    <servlet-name>Serve</servlet-name>
    <servlet-class>Serve</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>Upload</servlet-name>
    <url-pattern>/upload</url-pattern>
  </servlet-mapping>

  <servlet-mapping>
    <servlet-name>Serve</servlet-name>
    <url-pattern>/serve</url-pattern>
  </servlet-mapping>

</web-app>

Usa el servicio de imágenes con Blobstore

Con el servicio de imágenes, se puede usar un valor de Blobstore como la fuente de una transformación. La imagen fuente puede ser tan grande como el tamaño máximo de un valor de Blobstore. El servicio de imágenes aún muestra la imagen transformada a la aplicación, por lo que esta imagen debe ser menor de 32 megabytes. Esto es útil para crear imágenes en miniatura de fotografías grandes subidas por los usuarios.

Para obtener información sobre el uso de servicios de imagen con valores Blobstore, consulta la documentación sobre el servicio de imágenes.

Usar la API de Blobstore con Google Cloud Storage

Puedes usar la API de Blobstore para almacenar BLOB en Cloud Storage en lugar de almacenarlos en Blobstore. Debes configurar un bucket como se describe en la documentación de Google Cloud Storage y especificar el bucket y el nombre de archivo en el BlobstoreService createUploadUrl. También especifica el nombre del bucket en el parámetro UploadOptions. En el controlador de carga, debes procesar los metadatos de FileInfo que se muestran y almacenar de forma explícita el nombre de archivo de Google Cloud Storage necesario para recuperar el BLOB más adelante.

También puedes entregar objetos de Cloud Storage mediante la API de Blobstore.

Los siguientes fragmentos de código muestran cómo hacerlo. Esta muestra se encuentra en un controlador de solicitudes que obtiene el nombre del bucket y el nombre del objeto en la solicitud. Este crea el servicio de Blobstore y lo usa a fin de crear una clave de BLOB para Google Cloud Storage mediante el bucket y el nombre del objeto suministrados:

BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();
BlobKey blobKey = blobstoreService.createGsBlobKey(
    "/gs/" + fileName.getBucketName() + "/" + fileName.getObjectName());
blobstoreService.serve(blobKey, resp);

Cuotas y límites

El espacio usado por los valores de Blobstore contribuyen a la cuota Datos almacenados (facturable). Las entidades de información de blob en el almacén de datos cuentan con los límites relacionados con el almacén de datos. Ten en cuenta que Google Cloud Storage es un servicio que se paga según su uso. Se te cobrará de acuerdo con la hoja de precios de Cloud Storage.

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

Además de las cuotas de seguridad para todo el sistema, los límites siguientes se aplican en específico al uso de Blobstore:

  • 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.
  • La cantidad máxima de archivos que pueden subirse en un solo formulario POST es de 500.