了解如何使用任务队列和 App Engine Image API 来调整图片大小。
任务队列无需用户直接交互即可执行代码,同时支持任务在后台运行。在为 Cloud Storage 添加图片后,本指南使用任务队列执行任务。 任务队列中要执行的任务如下:
- 检索刚刚上传到 Cloud Storage 的图片文件。
- 使用 Image API 将其调整为缩略图。
- 将生成的缩略图存储在 Cloud Storage 中。
App Engine Java 8 运行时还支持 Java 的原生图片处理类,例如 AWT 和 Java2D。
准备工作
本指南使用的是 Apache Commons IOUtils 库。要将 IOUtils 库添加到您的 App Engine 项目中,请执行以下操作:
添加到您的
pom.xml
:<dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.5</version> </dependency>
导入库
本指南中提供的示例代码使用以下导入项:
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;
创建任务队列
App Engine 提供了 default
任务队列,但您可以为不同类型的工作创建不同的任务队列。例如,您可以创建一个任务队列来调整图片大小,另外创建一个任务队列来更新应用的数据库。
如需添加队列,请在 App Engine 项目的 WEB-INF
目录中创建 queue.xml
文件。任务队列必须指定名称和执行速率:
<?xml version="1.0" encoding="UTF-8"?>
<queue-entries>
<queue>
<name>resize-image</name>
<rate>60/h</rate>
</queue>
</queue-entries>
上述名为 resize-image
的示例队列将执行速率指定为每小时 60 次,即每分钟一次。如需查看完整的队列选项列表,请参阅 queue.xml
参考文档。
任务队列由两部分组成:任务请求程序和任务处理程序。请求程序用于将任务添加到队列并将其发送给任务处理程序。
将任务添加到队列
要将任务添加到队列,请执行以下操作:
使用
QueueFactory.getQueue()
创建任务队列对象,并确保指定queue.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. } }
将任务添加到
Queue
对象。如代码示例所示,imageResizeQueue.add()
将任务添加到imageResizeQueue
对象:try { // Add a queued task to create a thumbnail of the uploaded image imageResizeQueue.add( TaskOptions.Builder.withUrl("/tasks/imageresize").param("filename", filename)); }
使用
TaskOptions.Builder.withUrl()
指定任务处理程序的 URI,并指定发送给处理程序的任何参数。在此示例中,URI 为
/tasks/imageresize
,而参数是名为filename
的变量,其中包含待处理图片的文件名。
创建任务处理程序
将任务添加到队列后,映射至 URI /tasks/imageresize
的任务处理程序将运行。任务处理程序是一个尝试执行任务(直到成功执行)的 Java Servlet。
在本示例中,任务处理程序将执行以下三项任务:
从 Cloud Storage 中检索调用程序指定的图片。
使用 App Engine Image API 转换图片,在本示例中是转换为缩略图。
将转换后的图片(缩略图)存储在 Cloud Storage 中。
要创建任务处理程序,请执行以下操作:
添加将处理程序映射到 URI
/tasks/imageresize
的注释:@WebServlet(name = "imageResize", description = "Task queue handler", urlPatterns = "/tasks/imageresize") public class imageResize extends HttpServlet { // Task handler functionality }
按照 Cloud Storage 使用指南中的说明建立与 Cloud Storage 的连接,并从 Cloud Storage 中检索图片:
public void init() throws ServletException { // initiate GcsService GcsService gcsService = GcsServiceFactory.createGcsService( new RetryParams.Builder() .initialRetryDelayMillis(10) .retryMaxAttempts(10) .totalRetryPeriodMillis(15000) .build()); }
处理传入的任务队列请求,同时使用提供的文件名从 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
使用
ImagesService
对象调整图片大小:// 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()));
上述代码段使用 Image API
makeResize()
方法将图片调整为缩略图。为此,该方法会从 Cloud Storage 中读取图片并将其写入InputChannel
,然后使用IOUtils.toByteArray()
将其转换为 ByteArray。转换完成后,新图片的文件名中将附加字符串
thumbnail_
,其权限设置为可公开读取并写入 Cloud Storage。
保护任务处理程序网址
您应保护好用于执行敏感操作的任务,例如通过修改数据来使外部用户无法直接调用它们。为此,您可以设置限制,只允许 App Engine 管理员访问任务,从而阻止用户访问任务网址。请注意,这种限制不适用于来自 App Engine 应用的任务请求。
在本示例中,任务处理程序的网址位于 /tasks/
文件夹中。
如需限制 App Engine 管理员对 /tasks/
文件夹的访问,请将以下内容添加到项目的 web.xml
。
<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>
从队列中移除单个任务
如需从队列中移除单个任务,请使用 deleteTask()
:
private void removeTask(Queue queue, String taskName) {
queue.deleteTask(taskName); // remove a task from a queue
}
从队列中移除所有任务
如需从队列中移除所有任务,请使用 purge()
。完全清除队列中的所有任务最多可能需要一分钟的时间。
private void purgeQueue(Queue queue) {
queue.purge(); // remove all tasks from a queue
}
从队列中移除所有任务可能需要一分钟的时间,因此您应等待几秒钟,之后再向队列中添加新任务。
删除任务队列
如需删除任务队列,请从项目的 queue.xml
文件中移除该条目并重新部署。
部署到 App Engine
您可以使用 Maven 将应用部署到 App Engine。
转到项目的根目录并输入以下命令:
mvn package appengine:deploy -Dapp.deploy.projectId=PROJECT_ID
将 PROJECT_ID 替换为您的 Google Cloud 项目的 ID。 如果您的 pom.xml
文件已经指定了您的项目 ID,则您无需在运行的命令中添加 -Dapp.deploy.projectId
属性。
Maven 完成应用部署后,输入以下命令可在新应用中自动打开网络浏览器标签:
gcloud app browse
后续步骤
本指南介绍了如何使用任务队列来创建图片缩略图并将其存储在 Cloud Storage 中。该服务可与其他存储服务(如 Cloud Datastore 或 Cloud SQL)结合使用。