Usar Cloud Storage con Python

En esta parte del tutorial de Bookshelf, se explica cómo la aplicación de muestra almacena imágenes en Google Cloud Storage.

Esta página forma parte de un tutorial de varias páginas. Para empezar en orden y consultar las instrucciones de configuración, ve a la sección relativa a la aplicación Bookshelf para Python.

Crear un segmento de Cloud Storage

Cloud Storage te permite almacenar y publicar datos binarios. En este sentido, un segmento es un contenedor de alto nivel para objetos binarios.

A continuación, se explica cómo crear un segmento de Cloud Storage. Los segmentos son los contenedores básicos que almacenan tus datos en Cloud Storage.

Para crear un segmento, sigue estas instrucciones:

  1. Invoca el siguiente comando en una ventana de terminal:

    gsutil mb gs://[YOUR-BUCKET-NAME]

  2. Establece la LCA predeterminada del segmento como public-read; con ello, los usuarios podrán ver las imágenes que suban:

    gsutil defacl set public-read gs://[YOUR-BUCKET-NAME]

Establecer configuración

En esta sección, se usa el código del directorio 3-binary-data. Edita los archivos y ejecuta los comandos de este directorio.

  1. Abre config.py para editarlo.
  2. Utiliza el ID del proyecto, que puede verse en la consola de GCP, como el valor de PROJECT_ID.
  3. Define DATA_BACKEND con el mismo valor que empleaste en el tutorial sobre el uso de datos estructurados.
  4. Si utilizas Cloud SQL o MongoDB, usa los mismos valores que empleaste en el paso sobre el uso de datos estructurados en las secciones Cloud SQL o Mongo.
  5. En CLOUD_STORAGE_BUCKET, establece como valor el nombre del segmento de Cloud Storage.

  6. Guarda y cierra config.py.

Si usas Cloud SQL, sigue estos pasos:

  1. Abre app.yaml para editarlo.
  2. En cloud_sql_instances, establece el mismo valor que utilizaste para CLOUDSQL_CONNECTION_NAME en config.py . Debe tener el formato project:region:cloudsql-instance. Elimina los comentarios de toda esta línea.
  3. Guarda y cierra app.yaml.

Instalar dependencias

Introduce los comandos siguientes para crear un entorno virtual e instalar las dependencias:

Linux/macOS X

virtualenv -p python3 env
source env/bin/activate
pip install -r requirements.txt

Windows

virtualenv -p python3 env
env\scripts\activate
pip install -r requirements.txt

Ejecutar la aplicación en la máquina local

  1. Inicia un servidor web local:

    python main.py
    
  2. Introduce la siguiente dirección en el navegador web:

    http://localhost:8080

Ahora puedes navegar por las páginas web de la aplicación y editar libros, eliminarlos o añadir otros con imágenes de portada.

Presiona Control + C para salir del servidor web local.

Desplegar la aplicación en el entorno flexible de App Engine

  1. Despliega la aplicación de muestra:

    gcloud app deploy
    
  2. Introduce la siguiente dirección en el navegador web. Sustituye [YOUR_PROJECT_ID] por el ID del proyecto:

    https://[YOUR_PROJECT_ID].appspot.com
    

Si actualizas la aplicación, podrás desplegar la versión actualizada con el mismo comando que utilizaste para desplegar la aplicación por primera vez. El nuevo despliegue crea una nueva versión de la aplicación y la establece como la versión predeterminada. No obstante, se conservarán las versiones anteriores, al igual que las instancias de máquina virtual asociadas. Ten en cuenta que todas estas versiones de la aplicación y las instancias de máquina virtual son recursos facturables.

Si eliminas las versiones no predeterminadas de la aplicación, puedes reducir los costes.

Para eliminar una versión de la aplicación, sigue las instrucciones que figuran a continuación:

  1. In the GCP Console, go to the Versions page for App Engine.

    Go to the Versions page

  2. Select the checkbox for the non-default app version you want to delete.
  3. Click Delete to delete the app version.

Consulta la sección sobre cómo eliminar los recursos facturables en el último paso de este tutorial para obtener más información al respecto.

Estructura de la aplicación

Estructura de muestra de datos binarios

La aplicación usa Cloud Storage para almacenar datos binarios (imágenes en este caso) y, al mismo tiempo, sigue utilizando una base de datos estructurada (ya sea Cloud Datastore, Cloud SQL o MongoDB) para la información de los libros.

Información sobre el código

En esta sección, se explica detalladamente el código de la aplicación y su funcionamiento.

Administración de las subidas de los usuarios

Ahora los usuarios podrán subir imágenes gracias a la modificación del formulario de añadir/editar, que permitirá subir archivos mediante la definición del valor de enctype como multipart/form-data. Además, se ha añadido un nuevo campo para la imagen.

{% extends "base.html" %}

{% block content %}
<h3>{{action}} book</h3>

<form method="POST" enctype="multipart/form-data">

  <div class="form-group">
    <label for="title">Title</label>
    <input type="text" name="title" id="title" value="{{book.title}}" class="form-control"/>
  </div>

  <div class="form-group">
    <label for="author">Author</label>
    <input type="text" name="author" id="author" value="{{book.author}}" class="form-control"/>
  </div>

  <div class="form-group">
    <label for="publishedDate">Date Published</label>
    <input type="text" name="publishedDate" id="publishedDate" value="{{book.publishedDate}}" class="form-control"/>
  </div>

  <div class="form-group">
    <label for="description">Description</label>
    <textarea name="description" id="description" class="form-control">{{book.description}}</textarea>
  </div>

  <div class="form-group">
    <label for="image">Cover Image</label>
    <input type="file" name="image" id="image" class="form-control"/>
  </div>

  <div class="form-group hidden">
    <label for="imageUrl">Cover Image URL</label>
    <input type="text" name="imageUrl" id="imageUrl" value="{{book.imageUrl}}" class="form-control"/>
  </div>

  <button type="submit" class="btn btn-success">Save</button>
</form>

{% endblock %}

El framework de Flask tiene una funcionalidad incorporada para analizar las subidas de archivos. Flask hace que el objeto de archivo esté disponible en el campo files del objeto de la petición. Como es posible que el valor de image no exista porque el usuario no ha subido ningún archivo, se utiliza el método get para acceder al campo en lugar de la sintaxis habitual del diccionario. De esta forma, si no hay ningún archivo, se devuelve None en lugar de un KeyError.

image_url = upload_image_file(request.files.get('image'))

La llamada upload_image_file realiza el envío a una función auxiliar, también en crud.py, que invoca la función storage.upload_file con el segmento de Cloud apropiado y las extensiones permitidas definidas en el archivo de configuración.

def upload_image_file(file):
    """
    Upload the user-uploaded file to Google Cloud Storage and retrieve its
    publicly-accessible URL.
    """
    if not file:
        return None

    public_url = storage.upload_file(
        file.read(),
        file.filename,
        file.content_type
    )

    current_app.logger.info(
        "Uploaded file %s as %s.", file.filename, public_url)

    return public_url

Subidas en Cloud Storage

Una vez que crud.py llama a storage.upload_file para gestionar la subida de los archivos en Cloud Storage, el código comprueba una extensión de archivo de imagen para asegurarse de que solo se suban imágenes. Dado que la imagen se aloja en Cloud Storage, no existe el riesgo de que un archivo malicioso cause daños en la aplicación, pero sigue siendo recomendable permitir solo los tipos de archivos adecuados. Ten en cuenta el uso de la biblioteca six para mantener la compatibilidad tanto con Python 2 como con Python 3.

def upload_file(file_stream, filename, content_type):
    """
    Uploads a file to a given Cloud Storage bucket and returns the public url
    to the new object.
    """
    _check_extension(filename, current_app.config['ALLOWED_EXTENSIONS'])
    filename = _safe_filename(filename)

    client = _get_storage_client()
    bucket = client.bucket(current_app.config['CLOUD_STORAGE_BUCKET'])
    blob = bucket.blob(filename)

    blob.upload_from_string(
        file_stream,
        content_type=content_type)

    url = blob.public_url

    if isinstance(url, six.binary_type):
        url = url.decode('utf-8')

    return url

La función upload_file proporciona la URL pública de la imagen devuelta por Cloud Storage. Luego, la URL se guarda en la base de datos.

if image_url:
    data['imageUrl'] = image_url

Publicación de imágenes desde Cloud Storage

Como disponemos de la URL pública de la imagen, esta se puede publicar con facilidad. Resulta muy útil publicar el contenido directamente desde Cloud Storage, ya que las peticiones aprovechan la infraestructura de publicación global de Google y la aplicación no tiene que responder a las peticiones de imágenes. De este modo, se liberan ciclos de CPU para otras peticiones.

<div class="media-left">
  {% if book.imageUrl %}
    <img class="book-image" src="{{book.imageUrl}}">
  {% else %}
    <img class="book-image" src="http://placekitten.com/g/128/192">
  {% endif %}
</div>
¿Te ha resultado útil esta página? Enviar comentarios:

Enviar comentarios sobre...