public async Task <APIGatewayProxyResponse> AddImageAsync(APIGatewayProxyRequest request, ILambdaContext context) { context.Logger.LogLine($"POST /images body: {request.Body}"); var imageRequest = JsonSerializer.Deserialize <AddImageRequestModel>(request?.Body); var fileExt = Path.GetExtension(imageRequest.FileName); var image = new ScreenImage { Type = imageRequest.ImageType, UploadedOn = DateTime.UtcNow, UserName = "******", // TODO: set from auth'd user // IsPublished is false until the image has been processed }; image.FileName = $"{image.Id}{fileExt}".ToLower(); var keyPrefix = $"{UPLOAD_KEY_PREFIX}{image.UploadedOn.ToString("yyyy/MM")}"; var imageFileKey = $"{keyPrefix}/{image.FileName}"; // All of these properties, including meta-data must match what the // frontend is sending ootherwise the key will mismatch, resulting // in a 403 response from S3 var presignRequest = new GetPreSignedUrlRequest { Verb = HttpVerb.PUT, BucketName = _bucketName, Key = imageFileKey, ContentType = imageRequest.ContentType, Expires = DateTime.UtcNow.AddSeconds(PRESIGN_URL_EXPIRATION_SECS), }; presignRequest.Metadata.Add("imageId", image.Id); presignRequest.Metadata.Add("imageType", image.Type); presignRequest.Metadata.Add("userName", image.UserName); // Maybe use user ID instead? var presignedUrl = _s3Client.GetPreSignedURL(presignRequest); context.Logger.LogLine($"Saving image with ID {image.Id}"); await _ddbService.SaveAsync(image); var addImageResponse = new AddImageResponseModel { Id = image.Id, Key = imageFileKey, Type = image.Type, UploadUrl = presignedUrl }; var response = new APIGatewayProxyResponse { StatusCode = (int)HttpStatusCode.Created, Body = JsonSerializer.Serialize(addImageResponse), Headers = _defaultHeaders }; return(response); }
public async Task <string> ProcessImage(S3Event evnt, ILambdaContext context) { var s3Event = evnt.Records?[0].S3; if (s3Event == null) { context.Logger.LogLine("ERROR: Could not get S3 Event Data"); return(null); } try { var objMetadata = await _s3Client.GetObjectMetadataAsync(s3Event.Bucket.Name, s3Event.Object.Key); var imageId = objMetadata.Metadata["x-amz-meta-imageid"]; var imageType = objMetadata.Metadata["x-amz-meta-imagetype"]; context.Logger.LogLine($"Processing image {s3Event.Object.Key}. ImageType: {imageType ?? "unknown"}"); // Copy the file from the upload folder var originalSizeKey = GetResizedFileKey(s3Event.Object.Key); await _s3Client.CopyObjectAsync(s3Event.Bucket.Name, s3Event.Object.Key, s3Event.Bucket.Name, originalSizeKey); // Ideally, we would use the stream instead, but couldn't figure out how to get // it to re-use the same stream byte[] imageBytes = null; var imageContentType = ""; using (var objectResp = await _s3Client.GetObjectAsync(s3Event.Bucket.Name, s3Event.Object.Key)) using (var ms = new MemoryStream()) { imageContentType = objectResp.Headers.ContentType; await objectResp.ResponseStream.CopyToAsync(ms); imageBytes = ms.ToArray(); } var imageTypeSizes = _sizes.Where(x => x.ImageType == imageType); foreach (var imgSize in imageTypeSizes) { context.Logger.LogLine($"... Resizing to {imgSize.Key}"); IImageFormat imageFormat; using (var image = Image.Load(imageBytes, out imageFormat)) { var resizedFileKey = GetResizedFileKey(s3Event.Object.Key, imgSize.Key); using (var outStream = new MemoryStream()) { image.Mutate(x => x.Resize(new ResizeOptions { Mode = ResizeMode.Max, // TODO: allow each profile to specify method used Position = AnchorPositionMode.Center, Size = new Size(imgSize.Width, imgSize.Height) })); image.Save(outStream, imageFormat); var putObjectRequest = new PutObjectRequest { Key = resizedFileKey, BucketName = s3Event.Bucket.Name, ContentType = imageContentType, InputStream = outStream }; await _s3Client.PutObjectAsync(putObjectRequest); } } context.Logger.LogLine($"... Resized and saved file '{s3Event.Object.Key}' to '{imgSize.Key}'"); } var imageRecord = await _ddbService.GetByIdAsync <ScreenImage>(imageId); if (imageRecord != null) { imageRecord.IsPublished = true; await _ddbService.SaveAsync(imageRecord); } // Delete the original await _s3Client.DeleteObjectAsync(s3Event.Bucket.Name, s3Event.Object.Key); return("Ok"); } catch (Exception ex) { context.Logger.LogLine($"Error processing image s3://{s3Event.Bucket.Name}/{s3Event.Object.Key}. Error: {ex.Message}"); context.Logger.LogLine(ex.StackTrace); throw; } }