首页 > 技术文章 > AWS Lambda学习2:通过S3事件触发调用Lambda函数,实现缩略图地生成

wenjiayi 2020-04-23 16:35 原文

创建一个Amazon Lambda函数,实现从S3源存储桶读取图片对象并创建缩略图到目标存储桶,开发实现过程如下:

1、创建存储桶并上传示例对象

(1)打开 Amazon S3 控制台

(2)创建两个存储桶。源存储桶lambda-demo1-bucket,目标存储桶lambda-demo1-bucketresized。

(3)在源存储桶中,上传一个 .jpg 对象 sidatianwang.jpg。

2、创建Lambda函数

(1)创建Maven项目,pom.xml中添加如下:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
       <modelVersion>4.0.0</modelVersion>
       <groupId>com.lenovo</groupId>
       <artifactId>lambda</artifactId>
       <version>0.0.1-SNAPSHOT</version>
       <packaging>jar</packaging>
       <name>lambda</name>
       <url>http://maven.apache.org</url>
       <properties>
              <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
       </properties>
 
       <dependencies>
              <dependency>
                     <groupId>junit</groupId>
                     <artifactId>junit</artifactId>
                     <version>3.8.1</version>
                     <scope>test</scope>
              </dependency>
              <dependency>
                     <groupId>net.lingala.zip4j</groupId>
                     <artifactId>zip4j</artifactId>
                     <version>1.3.2</version>
              </dependency>
              <dependency>
                     <groupId>com.amazonaws</groupId>
                     <artifactId>aws-lambda-java-core</artifactId>
                     <version>1.2.0</version>
              </dependency>
              <dependency>
                     <groupId>com.amazonaws</groupId>
                     <artifactId>aws-lambda-java-events</artifactId>
                     <version>2.2.2</version>
              </dependency>
              <dependency>
                     <groupId>com.amazonaws</groupId>
                     <artifactId>aws-java-sdk-core</artifactId>
                     <version>1.11.520</version>
              </dependency>
              <dependency>
                     <groupId>com.amazonaws</groupId>
                     <artifactId>aws-java-sdk-s3</artifactId>
                     <version>1.11.520</version>
              </dependency>
              <dependency>
                     <groupId>com.amazonaws</groupId>
                     <artifactId>aws-java-sdk-lambda</artifactId>
                     <version>1.11.520</version>
              </dependency>
              <dependency>
                     <groupId>com.amazonaws</groupId>
                     <artifactId>aws-java-sdk-codebuild</artifactId>
                     <version>1.11.745</version>
              </dependency>
              <!-- <dependency>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-shade-plugin</artifactId>
                     <version>3.2.1</version>
              </dependency> -->
       </dependencies>
 
       <build>
              <plugins>
                     <plugin>
                            <groupId>org.apache.maven.plugins</groupId>
                            <artifactId>maven-shade-plugin</artifactId>
                            <version>2.3</version>
                            <configuration>                                     <createDependencyReducedPom>false</createDependencyReducedPom>
                            </configuration>
                            <executions>
                                  <execution>
                                          <phase>package</phase>
                                          <goals>
                                                 <goal>shade</goal>
                                          </goals>
                                   </execution>
                            </executions>
                     </plugin>
              </plugins>
       </build>
</project>

(2)编写主函数,接收Amazon S3事件输入并对其包含的消息进行处理。

package example;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.imageio.ImageIO;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.S3Event;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.event.S3EventNotification.S3EventNotificationRecord;
import com.amazonaws.services.s3.model.GetObjectRequest;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.S3Object;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;

public class Handler implements RequestHandler<S3Event, String> {
	private static final float MAX_WIDTH = 100;
	private static final float MAX_HEIGHT = 100;
	private final String JPG_TYPE = (String) "jpg";
	private final String JPG_MIME = (String) "image/jpeg";
	private final String PNG_TYPE = (String) "png";
	private final String PNG_MIME = (String) "image/png";

	public String handleRequest(S3Event s3event, Context context) {
		try {
			S3EventNotificationRecord record = s3event.getRecords().get(0);

			String srcBucket = record.getS3().getBucket().getName();

			// Object key may have spaces or unicode non-ASCII characters.
			String srcKey = record.getS3().getObject().getUrlDecodedKey();

			String dstBucket = srcBucket + "resized";
			String dstKey = "resized-" + srcKey;

			// Sanity check: validate that source and destination are different
			// buckets.
			if (srcBucket.equals(dstBucket)) {
				System.out.println("Destination bucket must not match source bucket.");
				return "";
			}

			// Infer the image type.
			Matcher matcher = Pattern.compile(".*\\.([^\\.]*)").matcher(srcKey);
			if (!matcher.matches()) {
				System.out.println("Unable to infer image type for key " + srcKey);
				return "";
			}
			String imageType = matcher.group(1);
			if (!(JPG_TYPE.equals(imageType)) && !(PNG_TYPE.equals(imageType))) {
				System.out.println("Skipping non-image " + srcKey);
				return "";
			}
			
			// Download the image from S3 into a stream
			AmazonS3 s3Client = AmazonS3ClientBuilder.defaultClient();
			S3Object s3Object = s3Client.getObject(new GetObjectRequest(srcBucket, srcKey));
			InputStream objectData = s3Object.getObjectContent();

			// Read the source image
			BufferedImage srcImage = ImageIO.read(objectData);
			int srcHeight = srcImage.getHeight();
			int srcWidth = srcImage.getWidth();
			// Infer the scaling factor to avoid stretching the image
			// unnaturally
			float scalingFactor = Math.min(MAX_WIDTH / srcWidth, MAX_HEIGHT / srcHeight);
			int width = (int) (scalingFactor * srcWidth);
			int height = (int) (scalingFactor * srcHeight);

			BufferedImage resizedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
			Graphics2D g = resizedImage.createGraphics();
			// Fill with white before applying semi-transparent (alpha) images
			g.setPaint(Color.white);
			g.fillRect(0, 0, width, height);
			// Simple bilinear resize
			g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
			g.drawImage(srcImage, 0, 0, width, height, null);
			g.dispose();

			// Re-encode image to target format
			ByteArrayOutputStream os = new ByteArrayOutputStream();
			ImageIO.write(resizedImage, imageType, os);
			InputStream is = new ByteArrayInputStream(os.toByteArray());
			// Set Content-Length and Content-Type
			ObjectMetadata meta = new ObjectMetadata();
			meta.setContentLength(os.size());
			if (JPG_TYPE.equals(imageType)) {
				meta.setContentType(JPG_MIME);
			}
			if (PNG_TYPE.equals(imageType)) {
				meta.setContentType(PNG_MIME);
			}
			
			// Uploading to S3 destination bucket
			System.out.println("Writing to: " + dstBucket + "/" + dstKey);
			try {
				s3Client.putObject(dstBucket, dstKey, is, meta);
			} catch (AmazonServiceException e) {
				System.err.println(e.getErrorMessage());
				System.exit(1);
			}
			System.out.println("Successfully resized " + srcBucket + "/" + srcKey + " and uploaded to " + dstBucket
					+ "/" + dstKey);
			return "Ok";
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}
}

(3)将maven工程打成jar包(以eclipse为例说明)

a. 清理,工程点击右键 → run as → Maven clean

b. 打包,工程点击右键 → run as → Maven install

在target目录下获取生成的jar包lambda-0.0.1-SNAPSHOT.jar

(4)登录Amazon lambda服务控制台,创建lambda测试函数

a. 名称随意;

b. 选择语言;

c. 选择或创建角色,角色要有lambda执行权限。

d. 上传jar包lambda-0.0.1-SNAPSHOT.jar

  注意处理程序命名规则:包.::方法名(例如:example.Handler::handleRequest

e. 添加S3触发器,通过S3事件触发调用lambda函数

f. 点击保存,保存lambda函数

3、配置S3事件通知

(1)打开 Amazon S3 控制台,依次执行:选择源存储桶→选择属性→高级选项卡下选择事件→添加通知

(2)在 Events (事件) 下,使用以下设置配置通知。

         名称 – lambda-trigger;

         事件 – ObjectCreate (All);

         发送到 – Lambda function;

         Lambda – test10。

4、点击测试,编写测试用例。

(1)点击配置测试事件,填写测试用例。(json格式,对应方法的参数)

{
  "Records": [
    {
      "eventVersion": "2.0",
      "eventSource": "aws:s3",
      "awsRegion": "ap-southeast-1",
      "eventTime": "1970-01-01T00:00:00.000Z",
      "eventName": "ObjectCreated:Put",
      "userIdentity": {
        "principalId": "EXAMPLE"
      },
      "requestParameters": {
        "sourceIPAddress": "127.0.0.1"
      },
      "responseElements": {
        "x-amz-request-id": "EXAMPLE123456789",
        "x-amz-id-2": "EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH"
      },
      "s3": {
        "s3SchemaVersion": "1.0",
        "configurationId": "testConfigRule",
        "bucket": {
          "name": "lambda-demo1-bucket",
          "ownerIdentity": {
            "principalId": "*"
          },
          "arn": "arn:aws:s3:::lambda-demo1-bucket"
        },
        "object": {
          "key": "sidatianwang.jpg",
          "size": 1024,
          "eTag": "0123456789abcdef0123456789abcdef",
          "sequencer": "0A1B2C3D4E5F678901"
        }
      }
    }
  ]
}

(2)点击测试按钮,即可运行

(3)返回Amazon S3控制台,验证目标存储桶中是否生成缩略图

经过验证发现,将图片sidatianwang.jpg上传到S3源存储桶lambda-demo1-bucket后,通过S3事件触发lambda函数test10,在S3目标存储桶lambda-demo1-bucketresized中生成了缩略图resized-sidatianwang.jpg。

推荐阅读