Saiba como usar filas de tarefas e a API Image do App Engine para redimensionar imagens.
As filas de tarefas executam código fora da interação direta do utilizador, o que permite que as tarefas ocorram em segundo plano. Este guia usa uma fila de tarefas para realizar tarefas depois de adicionar uma imagem ao Cloud Storage. As tarefas a realizar na fila de tarefas são:
- Obtenha o ficheiro de imagem que acabou de ser carregado para o Cloud Storage.
- Redimensione-a para uma imagem de miniatura através da API Image.
- Armazenar a miniatura resultante no Cloud Storage.
O tempo de execução do Java 8 do App Engine também suporta classes de manipulação de imagens nativas do Java, como AWT e Java2D.
Antes de começar
Configure o ambiente de programação e crie o projeto do App Engine.
Este guia usa a biblioteca Apache Commons IOUtils. Para incluir a biblioteca IOUtils no seu projeto do App Engine:
Adicione-o à sua
pom.xml
:<dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.5</version> </dependency>
Importar bibliotecas
O exemplo de código fornecido com este guia usa as seguintes importações:
import com.google.appengine.api.images.Image;
import com.google.appengine.api.images.ImagesService;
import com.google.appengine.api.images.ImagesServiceFactory;
import com.google.appengine.api.images.Transform;
import org.apache.commons.io.IOUtils;
Criar uma fila de tarefas
Embora o App Engine forneça uma fila de tarefas default
, pode criar diferentes filas de tarefas para diferentes tipos de trabalho. Por exemplo, pode criar uma fila de tarefas para redimensionar imagens e outra para atualizar a base de dados da sua app.
Para adicionar filas, crie o ficheiro queue.xml
no diretório WEB-INF
do projeto do App Engine. Uma taskqueue tem de especificar um nome e uma taxa de execução:
<?xml version="1.0" encoding="UTF-8"?>
<queue-entries>
<queue>
<name>resize-image</name>
<rate>60/h</rate>
</queue>
</queue-entries>
Esta fila de exemplo, denominada resize-image
, define uma taxa de execução de 60 vezes por hora ou uma vez por minuto. Para ver a lista completa de opções de fila, consulte a referência queue.xml
.
Uma fila de tarefas tem dois componentes: o requerente de tarefas e um controlador de tarefas. O solicitador adiciona uma tarefa à fila e envia-a para o controlador de tarefas.
Adicionar tarefas a uma fila
Para adicionar uma tarefa a uma fila:
Crie um objeto de fila de tarefas com
QueueFactory.getQueue()
, certificando-se de que especifica o nome da fila definido emqueue.xml
:Queue imageResizeQueue; // Taskqueue queue @Override public void init() throws ServletException { // Setup Cloud Storage service gcsService = GcsServiceFactory.createGcsService( new RetryParams.Builder() .initialRetryDelayMillis(10) .retryMaxAttempts(10) .totalRetryPeriodMillis(15000) .build()); // Initialize the queue object with the specific queue imageResizeQueue = QueueFactory.getQueue([QUEUE-NAME]); // Cloud SQL connection setup try { final String url = System.getProperty("cloudsql"); // Cloud SQL server URI try { conn = DriverManager.getConnection(url); // Connect to the database Statement createTable; // SQL statement // Batch SQL table creation commands createTable.addBatch(createContentTableSql); createTable.addBatch(createUserTableSql); createTable.addBatch(createImageTableSql); createTable.addBatch(createBlogPostImageTableSql); conn.createTable.executeBatch(); // Execute batch } catch (SQLException e) { throw new ServletException("Unable to connect to Cloud SQL", e); } } finally { // Nothing really to do here. } }
Adicione tarefas ao objeto
Queue
. Conforme mostrado no exemplo de código,imageResizeQueue.add()
adiciona uma tarefa ao objetoimageResizeQueue
:try { // Add a queued task to create a thumbnail of the uploaded image imageResizeQueue.add( TaskOptions.Builder.withUrl("/tasks/imageresize").param("filename", filename)); }
Especifique o URI do controlador de tarefas através de
TaskOptions.Builder.withUrl()
, juntamente com os parâmetros enviados para o controlador.Neste exemplo, o URI é
/tasks/imageresize
e o parâmetro é uma variável denominadafilename
que contém o nome do ficheiro da imagem a ser processada.
Criar um controlador de tarefas
Depois de adicionar uma tarefa à fila, o controlador de tarefas mapeado para o URI
/tasks/imageresize
é executado. Um controlador de tarefas é um servlet Java que tenta
executar a tarefa até ter êxito.
Neste exemplo, o controlador de tarefas realiza três tarefas:
Obter a imagem especificada pelo autor da chamada a partir do Cloud Storage.
Transforme a imagem usando a API App Engine Image, neste exemplo, numa imagem em miniatura.
Armazenar a imagem transformada (miniatura) no Cloud Storage.
Para criar o controlador de tarefas:
Adicione uma anotação que mapeie o controlador para o URI
/tasks/imageresize
:@WebServlet(name = "imageResize", description = "Task queue handler", urlPatterns = "/tasks/imageresize") public class imageResize extends HttpServlet { // Task handler functionality }
Configure uma ligação ao Cloud Storage, conforme documentado no guia de utilização do Cloud Storage e obtenha a imagem do Cloud Storage:
public void init() throws ServletException { // initiate GcsService GcsService gcsService = GcsServiceFactory.createGcsService( new RetryParams.Builder() .initialRetryDelayMillis(10) .retryMaxAttempts(10) .totalRetryPeriodMillis(15000) .build()); }
Processar o pedido da fila de tarefas recebido, usando o nome do ficheiro fornecido para obter a imagem do Cloud Storage:
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String filename = req.getParameter("filename"); // Get the filename passed from the task requestor GcsFilename gcsFile = new GcsFilename(bucket, filename); // Create a valid Cloud Storage filename GcsInputChannel readChannel = gcsService.openPrefetchingReadChannel(gcsFile, 0, BUFFER_SIZE); // Read the file from Cloud Storage
Use o objeto
ImagesService
para redimensionar a imagem:// Get an instance of the ImagesService we can use to transform images. ImagesService imagesService = ImagesServiceFactory.getImagesService(); // Make an image directly from a byte array, and transform it. Image image = ImagesServiceFactory.makeImage(IOUtils.toByteArray(Channels.newInputStream(readChannel))); Transform resize = ImagesServiceFactory.makeResize(100, 50); // resize image to 100x50 Image resizedImage = imagesService.applyTransform(resize, image); // Write the transformed image back to a Cloud Storage object. gcsService.createOrReplace( new GcsFilename(bucket, "thumbnail_" + filename), new GcsFileOptions.Builder().acl("public-read").build(), ByteBuffer.wrap(resizedImage.getImageData()));
O fragmento acima usa o método
makeResize()
da API Image para redimensionar a imagem para uma miniatura. Para o fazer, lê a imagem do Cloud Storage numInputChannel
e converte-a num ByteArray através deIOUtils.toByteArray()
.Após aplicar a transformação, a nova imagem tem a string
thumbnail_
anexada ao nome do ficheiro, a autorização definida como publicamente legível e escrita no Cloud Storage.
Proteger URLs de controladores de tarefas
Deve proteger as tarefas que realizam operações confidenciais, como a modificação de dados, para que os utilizadores externos não as possam chamar diretamente. Pode fazê-lo restringindo o acesso às tarefas aos administradores do App Engine, o que impede os utilizadores de acederem aos URLs das tarefas . Tenha em atenção que esta restrição não se aplica a pedidos de tarefas provenientes da sua app do App Engine.
No exemplo atual, os controladores de tarefas têm URLs na pasta /tasks/
.
Para restringir o acesso à pasta /tasks/
aos administradores do App Engine, adicione o seguinte ao ficheiro web.xml
do projeto.
<security-constraint>
<web-resource-collection>
<web-resource-name>tasks</web-resource-name>
<url-pattern>/tasks/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>admin</role-name>
</auth-constraint>
</security-constraint>
Remover uma única tarefa de uma fila
Para remover uma única tarefa de uma fila, use deleteTask()
:
private void removeTask(Queue queue, String taskName) {
queue.deleteTask(taskName); // remove a task from a queue
}
Remover todas as tarefas de uma fila
Para remover todas as tarefas de uma fila, use purge()
. A limpeza pode demorar até 1 minuto a remover todas as tarefas na fila.
private void purgeQueue(Queue queue) {
queue.purge(); // remove all tasks from a queue
}
A remoção de todas as tarefas de uma fila pode demorar um minuto, pelo que deve aguardar alguns segundos antes de adicionar novas tarefas à fila.
Eliminar uma fila de tarefas
Para eliminar uma fila de tarefas, remova a entrada do ficheiro queue.xml
do projeto e volte a implementá-lo.
Implementação no App Engine
Pode implementar a sua app no App Engine através do Maven.
Aceda ao diretório raiz do seu projeto e escreva:
mvn package appengine:deploy -Dapp.deploy.projectId=PROJECT_ID
Substitua PROJECT_ID pelo ID do seu Google Cloud projeto. Se o seu ficheiro pom.xml
já
especificar o seu
ID do projeto, não precisa de incluir a propriedade -Dapp.deploy.projectId
no
comando que executar.
Depois de o Maven implementar a sua app, é aberto automaticamente um separador do navegador de Internet na nova app. Para tal, escreva:
gcloud app browse
O que se segue?
Este guia mostra como usar uma fila de tarefas para criar uma miniatura de imagem e armazená-la no Cloud Storage. Pode ser usado com outros serviços de armazenamento, como o Cloud Datastore ou o Cloud SQL.