æãæ°ãã€ããŠãã ãããç§ãã¡ã¯ãã€ã¯ããµãŒãã¹ã«é¢ããé©æ°çãªæ¬ã«åãçµãã§ããŸãããåæã«ããã®ãããã¯ã«ã€ããŠã€ã³ã¿ãŒãããã§æžãããã®ãèªãããšãå¿ããªãã§ãã ããã ãããã£ãŠãèè ã®Tim Wagnerããã€ã¯ããµãŒãã¹ã¢ãŒããã¯ãã£ã«é¢ãã圌ã®èŠè§£ãå ±æããŠããAmazonããã°ã®èå³æ·±ãèšäºãééããããšã¯ã§ããŸããã§ããã
å€ãã®JavaãJavaScriptãã¹ããŒããæšè«ãããã³å€§éã®ããã¹ã
LinuxCon / ContainerCon 2015ã«ã³ãã¡ã¬ã³ã¹ã§ã ã ãµãŒããŒã®ãªããã€ã¯ããµãŒãã¹ ããšãããã¢ã¬ããŒããçºè¡šããŸããã ãã®äžã§ãç»ååŠççšã®ãã€ã¯ããµãŒãã¹ã®äœæã«ã€ããŠèª¬æããããã€ãã®å°åã«å±éãããã®ãã€ã¯ããµãŒãã¹ããã·ã³ã€ã³ã¿ãŒãã§ã€ã¹ãšããŠäœ¿çšããã¢ãã€ã«ã¢ããªã±ãŒã·ã§ã³ãäœæããAmazon API GatewayãšWebãµã€ãã䜿çšããŠHTTPSããŒã¹ã®APIãè¿œå ããæ§ç¯å šäœãå®è¡ããŸãããŠãããããã³è² è·ãã¹ã-ãã¹ãŠãµãŒããŒãªãã
ãã®èšäºã§ã¯ãåè¿°ã®ã¬ããŒããå®å šã«åŸ©å ããããã€ãã®å Žæã§ã建ç¯ã®ç¹çŽ°ããæãäžããŠããŸãã è¿œå ã®å³ã¯ã¹ã©ã€ãã®éžæã«ãããŸãã ãã®ã¢ãŒããã¯ãã£ã®ãã1ã€ã®äŸã¯ãgistãªããžããªå ã®SquirrelBinå®è¡å¯èœãã¡ã€ã«ã§ãã
ãµãŒããŒã¬ã¹ã¢ãŒããã¯ãã£
ããµãŒããŒã¬ã¹ããšããçšèªã¯ãæ瀺çãªã¢ãŒããã¯ãã£ãäžèŠã§ããããšãæå³ããŸããã€ãŸãããµãŒããŒãªãã§ããµãŒããŒã«å±éããã«ãããã°ã©ã ãã€ã³ã¹ããŒã«ããã«å®è¡ã§ããŸãã ãããŒãžãã¯ã©ãŠããµãŒãã¹ãšã©ãããããã§ã®ã¿åäœããŸãã 以äžã®å³ã¯ãäž»èŠãªã³ã³ããŒãã³ããšãã®æ¥ç¶ã瀺ããŠããŸãããã·ã³ã€ã³ã¿ãŒãã§ã€ã¹ãšããŠã®ã©ã ãé¢æ°ãšãããã«çŽæ¥æ¥ç¶ããã¢ãã€ã«ã¢ããªã±ãŒã·ã§ã³ãããã³Amazon S3ã«ããéçãµã€ãã«HTTPãšã³ããã€ã³ããæäŸããAmazon API Gatewayã

AWS Lambdaã䜿çšããã¢ãã€ã«ããã³Webã¢ããªã±ãŒã·ã§ã³çšã®ãµãŒããŒã¬ã¹ã¢ãŒããã¯ãã£
ããã§ã¯å§ããŸãããïŒ
ã¹ããŒãž1ïŒç»ååŠççšã®ãµãŒãã¹ãäœæããŸã
ããã»ã¹å šäœãç°¡çŽ åããããã«ãLambdaãã¯ãããžãŒnodejsã«çµã¿èŸŒãŸããŠããImageMagickã©ã€ãã©ãªã䜿çšããŸãã ãã ããããã¯å¿ é ã§ã¯ãããŸãã-ç¬èªã®ã©ã€ãã©ãªãå¿ èŠãªå Žåã¯ã JavaScriptãŸãã¯ãã€ãã£ãã©ã€ãã©ãªãããŒããããã Pythonãå®è¡ããããã³ãã³ãã©ã€ã³ã®å®è¡å¯èœãã¡ã€ã«ã«ã³ãŒããã©ãããããã§ããŸã ã 次ã®äŸã¯nodejsã«å®è£ ãããŠããŸããã Java ã Clojure ã ScalaããŸãã¯AWS Lambdaã®å¥ã®jvmèšèªã䜿çšããŠãã®ãããªãµãŒãã¹ãäœæããããšãã§ããŸãã
以äžã®ã³ãŒãã¯ãImageMagickã®äžçš®ã®ããããŒã¯ãŒã«ãããšèŠãªãããšãã§ããŸã-ã³ãã³ãã®åºæ¬æ§é ïŒãã®ã³ãã³ãã¯ã¹ã€ãããªãã¬ãŒã¿ãŒïŒã«ç²Ÿéãããã©ã®åã蟌ã¿ç»åãæœåºããŠè¿ãããšãã§ããŸãã çµæã®ã³ãŒãã£ã³ã°ãšã¯å¥ã«ãä»ã®ãã¹ãŠãJSONã§æžãããšãã§ããŸãã
var im = require("imagemagick"); var fs = require("fs"); exports.handler = function(event, context) { if (event.operation) console.log("Operation " + event.operation + " requested"); switch (event.operation) { case 'ping': context.succeed('pong'); return; case 'getSample': event.customArgs = ["rose:", "/tmp/rose.png"]; im.convert(event.customArgs, function(err, output) { if (err) context.fail(err); else { var resultImgBase64 = new Buffer(fs.readFileSync("/tmp/rose.png")).toString('base64'); try {fs.unlinkSync("/tmp/rose.png");} catch (e) {} // discard context.succeed(resultImgBase64); } }); break; // default: var error = new Error('Unrecognized operation "' + event.operation + '"'); context.fail(error); return; } };
ãŸãããµãŒãã¹ãæ©èœããããšã確èªããŸãããã ãããè¡ãã«ã¯ã次ã®JSONãAWS Lambdaã³ã³ãœãŒã«ã®ãã¹ããŠã£ã³ããŠã«éä¿¡ããŸãã
{ "operation": "ping" }
å¿ é ã®ããã³ãå¿çãåä¿¡ããå¿ èŠããããŸãã 次ã«ãå®éã«ImageMagickåŒã³åºãã«é²ã¿ããã®ãããªJSONãéä¿¡ããŸãã
{ "operation": "getSample" }
ãã®ã¯ãšãªã¯ãbase64ã§ãšã³ã³ãŒããããPNG PNGã€ã¡ãŒãžã®æååè¡šçŸãååŸããŸãïŒââ iVBORw0KGg ... Jggg ==âã ããããåãªãã©ã³ãã æåã§ã¯ãªãããšã確èªããã«ã¯ãããããåãåããBase64ãç»åã«å€æãã䟿å©ãªãã³ãŒããŒïŒcodebeautify.org/base64-to-image-converterãªã©ïŒã«ïŒäºéåŒçšç¬Šãªãã§ïŒè²Œãä»ããŸãã ãã©ã®çŸããç»åãååŸããå¿ èŠããããŸãã

ãµã³ãã«ç»åïŒèµ€ããã©ïŒ
次ã«ãç»ååŠçãµãŒãã¹ãè¿œå ããŠãnodejsã©ãããŒã®æ®ãã®äœ¿çšãæºåããŸãã ããŸããŸãªæäœãæäŸããŸãã
- pingïŒãµãŒãã¹ã®å¯çšæ§ã確èªããŸãã
- getDimensionsïŒã€ã¡ãŒãžã®é«ããšå¹ ãååŸããããã«èå¥æäœãåŒã³åºãççž®çã
- èå¥ïŒç»åã®ã¡ã¿ããŒã¿ãååŸããŸãã
- ãµã€ãºå€æŽïŒãµã€ãºå€æŽã®ããã®è£å©çãªæé ïŒãå éšãå€æåŒã³åºãïŒ
- thumbnailïŒãµã€ãºå€æŽã®å矩èªã
- å€æïŒããŠãããŒãµã«ãæé -ã¡ãã£ã¢åœ¢åŒã®å€æãå€æã®é©çšããµã€ãºã®åèšç®ãªã©ãå¯èœ
- getSampleïŒãµã³ãã«ç»åãååŸããŸãã ãã®æäœã¯ãhello worldãã«å¯Ÿå¿ããŸã
ããã®ã³ãŒãã®ã»ãšãã©ã¯éåžžã«ç°¡åã§ãã ãã®ã³ãŒãã¯ãnodejsã«å®è£ ãããImageMagickããã·ãŒãžã£ãã©ãããããããã®äžéšã¯JSONãåãå ¥ããŸãïŒãã®å ŽåãLambdaã«ãã£ãŠéä¿¡ãããã€ãã³ãã¯ã¯ãªã¢ããããªãã€ã¬ã¯ããããŸãïŒããæååã®é åãšããŠæž¡ãããã³ãã³ãã©ã€ã³åŒæ°ïŒãããããã«ã¹ã¿ã ãïŒãåãå ¥ããŸãã ImageMagickã以åã«äœ¿çšããããšããªãå Žåããã®æ©èœã®1ã€ã®åŽé¢ã¯æããã§ã¯ãªããããããŸãããã€ãŸããã³ãã³ãã©ã€ã³ã®ã©ãããŒãšããŠæ©èœãããã¡ã€ã«åã«ã»ãã³ãã£ã¯ã¹ããããŸãã 競åãã2ã€ã®ããŒãºããããŸãã第1ã«ãã¯ã©ã€ã¢ã³ãã¯ã»ãã³ãã£ã¯ã¹ïŒããšãã°ãJPEGã«å¯ŸããPNGãªã©ã®åºåç»å圢åŒïŒã転éããå¿ èŠãããã第2ã«ããµãŒãã¹ã®äœæè ã¯ãã£ã¹ã¯äžã®äžæã¹ãã¬ãŒãžãä¿åããå Žæã決å®ããå¿ èŠãããããã詳现ã®æŒæŽ©ãèš±å¯ããŸããå®è£ ã ãããã®äž¡æ¹ã®åé¡ãåæã«è§£æ±ºããããã«ãJSONã¹ããŒã ã§2ã€ã®åŒæ°ãinputExtensionããšãoutputExtensionããå®çŸ©ããã¯ã©ã€ã¢ã³ãéšåïŒãã¡ã€ã«æ¡åŒµåïŒãšãµãŒããŒéšåïŒãã£ã¬ã¯ããªåããã³ããŒã¹åïŒãçµã¿åãããŠããã¡ã€ã«ã®å®éã®å ŽæãäœæããŸãã å®æããã³ãŒãã¯ã次ã®ç»ååŠçå³é¢ã§ç¢ºèªã§ããŸãïŒãããŠäœ¿çšã§ããŸãïŒïŒã
ããã§å®è¡ã§ãããã¹ãã¯å€æ°ãããŸããïŒä»¥äžã§å®è¡ããŸãïŒãæäœæ§ãç°¡åã«ãã¹ãããããã«ãããŒãºã®åãç»åãå床æœåºãããã¬ãã£ããã£ã«ã¿ãŒã䜿çšããŠè¿ããŸãïŒè²å転ãå®è¡ããŸãïŒã Lambdaã³ã³ãœãŒã«ã§åæ§ã®JSONãã¡ã€ã«ã䜿çšã§ããŸããbase64Imageãã£ãŒã«ãã®å 容ãç»åã«äžèŽããæåã«çœ®ãæããã ãã§ãïŒãã®ã·ãŒã±ã³ã¹ã¯éåžžã«é·ããªããŸãïŒã
{ "operation": "convert", "customArgs": [ "-negate" ], "outputExtension": "png", "base64Image": "...fill this in with the rose sample image, base64-encoded..." }
ç»åã«ãã³ãŒããããåºåã¯ãçã®æ€ç©ã®æŽç·Žãéããã©ã§ãïŒ

éããã©ïŒå ã®ç»åããèµ€ããã©ã§ãã¬ïŒ
ãµãŒãã¹ã®æ©èœã«é¢ããŠã¯ããã ãã§ãã ååãšããŠãããããåŒçšãéå§ããã1å皌åãããã24æé皌åã®ã¢ãã¿ãªã³ã°ãšãããã¯ã·ã§ã³ã®ãã®ã³ã°ãåããã¹ã±ãŒã©ãã«ã§ä¿¡é Œæ§ã®é«ããµãŒãã¹ãã«åãæ¿ããŸãã ãããããããLambdaã®çŸããã§ããç§ãã¡ã®ç»ååŠçã³ãŒãã¯ãã§ã«å®å šã«å±éããããã€ã¯ããµãŒãã¹ã§ãããå®çšåã®æºåãæŽã£ãŠããŸãã ãããåŒã³åºãããšãã§ããã¢ãã€ã«ã¢ããªã±ãŒã·ã§ã³ãè¿œå ããã«ã¯...
ã¹ããŒãž2ïŒã¢ãã€ã«ã¯ã©ã€ã¢ã³ããäœæãã
ã¢ããªã±ãŒã·ã§ã³ãåŠçããããã«ãã€ã¯ããµãŒãã¹ã«é£çµ¡ããæ¹æ³ã¯ããã€ããããŸããããµã³ãã«ã¯ã©ã€ã¢ã³ãã瀺ãããã«ãAndroidçšã®å°ããªã¢ããªã±ãŒã·ã§ã³ãäœæããŸãã 以äžã¯ãContainerConã¬ããŒãã§äœ¿çšãããã¯ã©ã€ã¢ã³ãã³ãŒãã§ãã ããã§ã¯ãç»åãšãã£ã«ã¿ãŒãååŸã§ããåçŽãªAndroidã¢ããªã±ãŒã·ã§ã³ãäœæããŸãããã®åŸããã£ã«ã¿ãŒããå€æãæäœã§ç»åã«é©çšãããæçµçã«äœãåŸãããããããããŸãã ãã£ã«ã¿ãªã³ã°ã¯ãAWS Lambdaã§åäœããç»ååŠçãã€ã¯ããµãŒãã¹ã§è¡ãããŸãã
ãã®ã¢ããªã±ãŒã·ã§ã³ã®æ©èœãæ確ã«ããããã«ãAWS Lambdaã¢ã€ã³ã³ãäŸã«ãšããŸãã

AWS Lambdaã¢ã€ã³ã³ã衚瀺ããAndroidãšãã¥ã¬ãŒã¿ãŒ
ããã¬ãã£ãããã£ã«ã¿ãŒãéžæããŠãã¢ã€ã³ã³ã®è²ãå転ããŸãã

ç»åå€æã«ããã¬ããã£ã«ã¿ãŒãéžæããŸã
...ãããŠãããã«çµæããããŸãïŒLambdaã¢ãã«ãŒã®éãããŒãžã§ã³ïŒå ã¯ãªã¬ã³ãžã§ããïŒïŒ

ãåŠå®ããã£ã«ã¿ãŒãAWS Lambdaã¢ã€ã³ã³ã«é©çšããçµæ
ããã«ãçŸä»£ã®ã·ã¢ãã«ã®ããã©ãã«ãã³ããŒãžã®å€èŠ³ãäžããããšãã§ããŸãã ã·ã¢ãã«ã®åçãæ®ããã»ãã¢ããŒã³ã®ãã£ã«ã¿ãŒãé©çšããŸãã

ã»ãã¢èª¿ã®ã·ã¢ãã«ã®ããã©ãã
ã³ãŒãã«æž¡ããŸãã ããã§ã¯ãAndroidã®ããã°ã©ãã³ã°ã®åºæ¬ã説æããã€ããã¯ãããŸããããã¢ããªã±ãŒã·ã§ã³ã®Lambdaåºæã®èŠçŽ ã«æ³šæããŠãã ããã ç¬èªã®ã¢ããªã±ãŒã·ã§ã³ãäœæããŠããå Žåã¯ãAWS Mobile SDKã¢ãŒã«ã€ããæå¹ã«ããŠã以äžã®ã³ãŒããµã³ãã«ãå®è¡ããå¿ èŠããããŸãã æŠå¿µçã«ã¯ãã³ãŒãã¯4ã€ã®éšåã§æ§æãããŠããŸãã
- POJOããŒã¿ã¹ããŒã
- ãªã¢ãŒããµãŒãã¹ã®å®çŸ©ïŒæäœïŒ
- åæå
- ãµãŒãã¹ã³ãŒã«
ãã¹ãŠã®éšåãé çªã«æ€èšããŠãã ããã
ããŒã¿ã¹ããŒã ã¯ãã¯ã©ã€ã¢ã³ããšãµãŒããŒéã§è»¢éããå¿ èŠããããã¹ãŠã®ãªããžã§ã¯ããå®çŸ©ããŸãã ãã©ã ã䞻矩ãã¯ãããŸããã ãã¹ãŠã®ãªããžã§ã¯ãã¯ãç¹å¥ãªã©ã€ãã©ãªããã¬ãŒã ã¯ãŒã¯ã®ãªãéåžžã®POJOïŒPlain Old Java ObjectïŒã§ãã åºã«ãªãã€ãã³ããå®çŸ©ãããããå±éããŠæäœã®æ§é ãåæ ããŸãã äžèšã®ç»ååŠçãµãŒãã¹ãå®çŸ©ããã³ãã¹ããããšãã«äœ¿çšããJSONã®ãjavificationãããããšæ³å®ã§ããŸãã JavaãµãŒããŒãäœæããå Žåã¯ãéåžžããããã®ãã¡ã€ã«ãå ±éã€ãã³ãæ§é ã®å®çŸ©ã®äžéšãšããŠå ±æããŸãã ãã®äŸã§ã¯ããããã®POJOã¯ãµãŒããŒåŽã®JSONã«å€æãããŸãã
LambdaEvent.java
package com.amazon.lambda.androidimageprocessor.lambda; public class LambdaEvent { private String operation; public String getOperation() {return operation;} public void setOperation(String operation) {this.operation = operation;} public LambdaEvent(String operation) {setOperation(operation);} }
ImageConvertRequest.java
package com.amazon.lambda.androidimageprocessor.lambda; import java.util.List; public class ImageConvertRequest extends LambdaEvent { private String base64Image; private String inputExtension; private String outputExtension; private List customArgs; public ImageConvertRequest() {super("convert");} public String getBase64Image() {return base64Image;} public void setBase64Image(String base64Image) {this.base64Image = base64Image;} public String getInputExtension() {return inputExtension;} public void setInputExtension(String inputExtension) {this.inputExtension = inputExtension;} public String getOutputExtension() {return outputExtension;} public void setOutputExtension(String outputExtension) {this.outputExtension = outputExtension;} public List getCustomArgs() {return customArgs;} public void setCustomArgs(List customArgs) {this.customArgs = customArgs;} }
ãããŸã§ã®ãšããããã¹ãŠã¯æ¯èŒçåçŽã§ãã ããŒã¿ã¢ãã«ãã§ããã®ã§ãããã€ãã®Javaã¢ãããŒã·ã§ã³ã䜿çšããŠãµãŒããŒãšã³ããã€ã³ããå®çŸ©ããŸãã ããã§ã¯ããpingããšãå€æããšãã2ã€ã®æäœãæäŸããŠããŸãã ä»ã®æäœãè¿œå ããŠã³ãŒããæ¡åŒµããããšã¯é£ãããããŸããããããã¯ä»¥äžã§èª¬æãããã¢ã¢ããªã±ãŒã·ã§ã³ã«ã¯å¿ èŠãããŸããã
ILambdaInvoker.java
package com.amazon.lambda.androidimageprocessor.lambda; import com.amazonaws.mobileconnectors.lambdainvoker.LambdaFunction; import java.util.Map; public interface ILambdaInvoker { @LambdaFunction(functionName = "ImageProcessor") String ping(Map event); @LambdaFunction(functionName = "ImageProcessor") String convert(ImageConvertRequest request); }
ããã§ãã¢ããªã±ãŒã·ã§ã³ã®äž»èŠéšåã«é²ãæºåãã§ããŸããã ããã§ã¯ãäž»ã«Androidã®ã¹ãã³ã·ã«ã³ãŒããšãã¯ã©ã€ã¢ã³ããªãœãŒã¹ãç°¡åã«ç®¡çããããã®ã³ãŒãã衚瀺ãããŸãããLambdaã«é¢é£ããããã€ãã®ãã©ã°ã¡ã³ãã«åå¥ã«æ³šç®ããŸãã
ããã¯ãinitãã»ã¯ã·ã§ã³ã§ãã ããã§ã¯ãLambda APIãåŒã³åºãèªèšŒãããã€ããŒãäœæãããLambda-invokerãäœæãããŸããããã«ãããäžèšã§å®çŸ©ããããšã³ããã€ã³ããåŒã³åºããPOJOãªããžã§ã¯ããããŒã¿ã¢ãã«ã«è»¢éã§ããŸãã
// CognitoCachingCredentialsProvider CognitoCachingCredentialsProvider cognitoProvider = new CognitoCachingCredentialsProvider( this.getApplicationContext(), "us-east-1:<YOUR COGNITO IDENITY POOL GOES HERE>", Regions.US_EAST_1); // LambdaInvokerFactory, Lambda. LambdaInvokerFactory factory = new LambdaInvokerFactory(this.getApplicationContext(), Regions.US_EAST_1, cognitoProvider); // - Lambda Json. lambda = factory.build(ILambdaInvoker.class);
ïŒéåžžã«ïŒèå³æ·±ããã1ã€ã®ã³ãŒãã¯ããªã¢ãŒãããã·ãŒãžã£ã³ãŒã«èªäœã§ãã
try { return lambda.convert(params[0]); } catch (LambdaFunctionException e) { Log.e("Tag", "Failed to convert image"); return null; }
å®éããã¹ãŠã®éæ³ïŒåŒæ°ã®ã·ãªã¢ã«åãšçµæã®éã·ãªã¢ã«åïŒã¯æé»çã«é²è¡ããããã§ããã€ãã®ãšã©ãŒåŠçãå®è¡ããã ããªã®ã§ãããã»ã©èå³æ·±ããã®ã§ã¯ãããŸããã
å®å šãªãœãŒã¹ãã¡ã€ã«ã次ã«ç€ºããŸãã
MainActivity.java
package com.amazon.lambda.androidimageprocessor; import android.app.Activity; import android.app.ProgressDialog; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.AsyncTask; import android.os.Bundle; import android.util.Base64; import android.util.Log; import android.view.View; import android.widget.ImageView; import android.widget.Spinner; import android.widget.Toast; import com.amazon.lambda.androidimageprocessor.lambda.ILambdaInvoker; import com.amazon.lambda.androidimageprocessor.lambda.ImageConvertRequest; import com.amazonaws.auth.CognitoCachingCredentialsProvider; import com.amazonaws.mobileconnectors.lambdainvoker.LambdaFunctionException; import com.amazonaws.mobileconnectors.lambdainvoker.LambdaInvokerFactory; import com.amazonaws.regions.Regions; import java.io.ByteArrayOutputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; public class MainActivity extends Activity { private ILambdaInvoker lambda; private ImageView selectedImage; private String selectedImageBase64; private ProgressDialog progressDialog; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // CognitoCachingCredentialsProvider CognitoCachingCredentialsProvider cognitoProvider = new CognitoCachingCredentialsProvider( this.getApplicationContext(), "us-east-1:2a40105a-b330-43cf-8d4e-b647d492e76e", Regions.US_EAST_1); // LambdaInvokerFactory, Lambda. LambdaInvokerFactory factory = new LambdaInvokerFactory(this.getApplicationContext(), Regions.US_EAST_1, cognitoProvider); // - Lambda Json. lambda = factory.build(ILambdaInvoker.class); // lambda-, , pingLambda(); } // lambda- @SuppressWarnings("unchecked") private void pingLambda() { Map event = new HashMap(); event.put("operation", "ping"); // Lambda- . // , . new AsyncTask<Map, Void, String>() { @Override protected String doInBackground(Map... params) { // "ping". , // LambdaFunctionException. try { return lambda.ping(params[0]); } catch (LambdaFunctionException lfe) { Log.e("Tag", "Failed to invoke ping", lfe); return null; } } @Override protected void onPostExecute(String result) { if (result == null) { return; } // Toast.makeText(MainActivity.this, "Made contact with AWS lambda", Toast.LENGTH_LONG).show(); } }.execute(event); } // "process image" public void processImage(View view) { // if (selectedImageBase64 == null) { Toast.makeText(this, "Please tap one of the images above", Toast.LENGTH_LONG).show(); return; } // String filter = ((Spinner) findViewById(R.id.filter_picker)).getSelectedItem().toString(); // ImageConvertRequest request = new ImageConvertRequest(); request.setBase64Image(selectedImageBase64); request.setInputExtension("png"); request.setOutputExtension("png"); // List customArgs = new ArrayList(); request.setCustomArgs(customArgs); switch (filter) { case "Sepia": customArgs.add("-sepia-tone"); customArgs.add("65%"); break; case "Black/White": customArgs.add("-colorspace"); customArgs.add("Gray"); break; case "Negate": customArgs.add("-negate"); break; case "Darken": customArgs.add("-fill"); customArgs.add("black"); customArgs.add("-colorize"); customArgs.add("50%"); break; case "Lighten": customArgs.add("-fill"); customArgs.add("white"); customArgs.add("-colorize"); customArgs.add("50%"); break; default: return; } // async- lambda- new AsyncTask() { @Override protected String doInBackground(ImageConvertRequest... params) { try { return lambda.convert(params[0]); } catch (LambdaFunctionException e) { Log.e("Tag", "Failed to convert image"); return null; } } @Override protected void onPostExecute(String result) { // , if (result == null || Objects.equals(result, "")) { hideLoadingDialog(); Toast.makeText(MainActivity.this, "Processing failed", Toast.LENGTH_LONG).show(); return; } // base64 byte[] imageData = Base64.decode(result, Base64.DEFAULT); selectedImage.setImageBitmap(BitmapFactory.decodeByteArray(imageData, 0, imageData.length)); hideLoadingDialog(); } }.execute(request); showLoadingDialog(); } /* */ public void selectLambdaImage(View view) { selectImage(R.drawable.lambda); selectedImage = (ImageView) findViewById(R.id.static_lambda); Toast.makeText(this, "Selected image 'lambda'", Toast.LENGTH_LONG).show(); } public void selectSeattleImage(View view) { selectImage(R.drawable.seattle); selectedImage = (ImageView) findViewById(R.id.static_seattle); Toast.makeText(this, "Selected image 'seattle'", Toast.LENGTH_LONG).show(); } public void selectSquirrelImage(View view) { selectImage(R.drawable.squirrel); selectedImage = (ImageView) findViewById(R.id.static_squirrel); Toast.makeText(this, "Selected image 'squirrel'", Toast.LENGTH_LONG).show(); } public void selectLinuxImage(View view) { selectImage(R.drawable.linux); selectedImage = (ImageView) findViewById(R.id.static_linux); Toast.makeText(this, "Selected image 'linux'", Toast.LENGTH_LONG).show(); } // 'id' , base64 private void selectImage(int id) { Bitmap bmp = BitmapFactory.decodeResource(getResources(), id); ByteArrayOutputStream stream = new ByteArrayOutputStream(); bmp.compress(Bitmap.CompressFormat.PNG, 100, stream); selectedImageBase64 = Base64.encodeToString(stream.toByteArray(), Base64.DEFAULT); } // public void reset(View view) { ((ImageView) findViewById(R.id.static_lambda)).setImageDrawable(getResources().getDrawable(R.drawable.lambda, getTheme())); ((ImageView) findViewById(R.id.static_seattle)).setImageDrawable(getResources().getDrawable(R.drawable.seattle, getTheme())); ((ImageView) findViewById(R.id.static_squirrel)).setImageDrawable(getResources().getDrawable(R.drawable.squirrel, getTheme())); ((ImageView) findViewById(R.id.static_linux)).setImageDrawable(getResources().getDrawable(R.drawable.linux, getTheme())); Toast.makeText(this, "Please choose from one of these images", Toast.LENGTH_LONG).show(); } private void showLoadingDialog() { progressDialog = ProgressDialog.show(this, "Please wait...", "Processing image", true, false); } private void hideLoadingDialog() { progressDialog.dismiss(); } }
ãããã¢ãã€ã«ã¢ããªã±ãŒã·ã§ã³å šäœã§ãã ããã¯ãããŒã¿ã¢ãã«ïŒJavaã¯ã©ã¹ïŒãå¶åŸ¡ã¢ãã«ïŒããã€ãã®ã¡ãœããïŒããã¹ãŠã®çš®é¡ãåæåãã3ã€ã®ã³ãã³ããããã³try / catchãããã¯ã§å²ãŸãããªã¢ãŒãåŒã³åºãã§æ§æãããŠããŸã...ç°¡åã§ãã
ããã€ãã®å°åã§ã®å±é
ãããŸã§ã®ãšããããã®ã³ãŒããæ©èœããå Žæã«ç¹ã«çŠç¹ãåãããŠããŸããã Lambdaã¯ã³ãŒãããªãŒãžã§ã³å ã«ãããã€ãã責任ããããŸããã䜿çšããä»ã®ãªãŒãžã§ã³ã決å®ããå¿ èŠããããŸãã æåã®ãã¢ã§ã¯ãç±³åœæ±éšã§æ©èœããé¢æ°ãäœæããŸãããããšãã°ãããã¯ããŒãžãã¢å·ã®ããŒã¿ã»ã³ã¿ãŒã«é©çšãããŸãã ã°ããŒãã«ãµãŒãã¹ãæäŸããŠããããšãäžèšã§æžããŸããããããã£ãŠããããã®å°åããã®ã¢ãã€ã«ã¢ããªã±ãŒã·ã§ã³ãæå°éã®é 延ã§ãµãŒãã¹ã«æ¥ç¶ã§ããããã«ããšãŒãããïŒã¢ã€ã«ã©ã³ãïŒããã³å€ªå¹³æŽå°åïŒæ±äº¬ïŒã«ã«ãã¬ããžãæ¡å€§ããŸãããã

2ã€ã®è¿œå ãªãŒãžã§ã³ã«Lambdaé¢æ°ããããã€ããããã®ãµãŒããŒã¬ã¹ã¡ã«ããºã
ãµãŒããŒã¬ã¹Webã¢ããªã±ãŒã·ã§ã³ããŒã1ïŒAPIãšã³ããã€ã³ã
ããã§ãã¢ãã€ã«ã¢ããªã±ãŒã·ã§ã³ãšããã®ãã·ã³ã€ã³ã¿ãŒãã§ã€ã¹ãšããŠæ©èœããã°ããŒãã«ã«å±éãããç»ååŠçãµãŒãã¹ãã§ããŸããã ããã€ã¹ã§ã¯ãªããã©ãŠã¶ã§äœæ¥ããããšã奜ãåå¿ã®ããã«ããµãŒããŒã¬ã¹Webã¢ããªã±ãŒã·ã§ã³ã®äœæã«ç§»ããŸãããã ããã¯2段éã§è¡ããŸãã ãŸããç»ååŠçãµãŒãã¹ã®APIãšã³ããã€ã³ããäœæããŸãã 次ã«ã次ã®ã»ã¯ã·ã§ã³ã§ãAmazon S3ã䜿çšããŠãµã€ãèªäœãè¿œå ããŸãã
AWS Lambdaã䜿çšãããšãã³ãŒãããµãŒãã¹ã«ç°¡åã«å€æã§ããŸããããã¯ãWebãµãŒãã¹ã¯ã©ã€ã¢ã³ãã€ã³ã¿ãŒãã§ã€ã¹ãããã§æ¢ã«ãçµã¿èŸŒãŸããŠãããããã§ãã ãã ããããã«ã¯ã¯ã©ã€ã¢ã³ãïŒåã®ã»ã¯ã·ã§ã³ã§äœæããã¢ãã€ã«ã¯ã©ã€ã¢ã³ããªã©ïŒãAWSããæäŸãããèªèšŒæ å ±ã§ãªã¯ãšã¹ãã«çœ²åããå¿ èŠããããŸãã ãã®ã¿ã¹ã¯ã¯ãAndroidã¢ããªã±ãŒã·ã§ã³ã§äœ¿çšãããAmazon CognitoèªèšŒã¯ã©ã€ã¢ã³ãã«ãã£ãŠè§£æ±ºãããŸããããŠã§ããµã€ããéããŠç»ååŠçãµãŒãã¹ãå ±æãããå Žåã¯ã©ãã§ããããïŒ
ãããè¡ãã«ã¯ãå¥ã®ãµãŒããŒã§ããAmazon API Gatewayã䜿çšããŸã ã ãã®ãµãŒãã¹ã䜿çšãããšãã€ã³ãã©ã¹ãã©ã¯ãã£ãå¿ èŠãšããã«APIãå®çŸ©ã§ããŸããAPIã¯AWSã«ãã£ãŠå®å šã«ç®¡çãããŸãã APIã²ãŒããŠã§ã€ã䜿çšããŠãç»ååŠçãµãŒãã¹ã§äœ¿çšãããURLãäœæããŸããããã«ãããWebãŠãŒã¶ãŒããã®æ©èœã®ãµãã»ããã«ã¢ã¯ã»ã¹ã§ããŸãã Amazon API Gatewayã¯ãããŸããŸãªAPIãžã®ã¢ã¯ã»ã¹ãå¶åŸ¡ããããŸããŸãªæ¹æ³ãæäŸããŸãïŒAPIåŒã³åºãã¯AWSèªèšŒæ å ±ã§çœ²åããããOAuthããŒã¯ã³ã䜿çšããŠããŒã¯ã³ããããŒãæ€èšŒã«ãªãã€ã¬ã¯ãããã ãã§ãAPIããŒã䜿çšã§ããŸãïŒã»ãã¥ãªãã£ã§ä¿è·ãããã¢ã¯ã»ã¹ãå¿ èŠãªå Žåã¯ãå§ãããŸããïŒãŸãã¯APIãå®å šã«äœæããŸãããã«ç€ºãããã«ãäžè¬å ¬éãããŠããŸãã
ã²ãŒããŠã§ã€APIã¯ãå€æ§ãªã¢ã¯ã»ã¹ã¢ãã«ã«å ããŠããã®èšäºã®ç¯å²ãè¶ ããå€ãã®æ©èœãæäŸããŸãã ãããã®äžéšã¯çµã¿èŸŒã¿ïŒDDOSæ»æã«å¯Ÿããä¿è·ãªã©ïŒã§ããã®ä»ã¯ãã£ãã·ã¥ãªã©ã«ããã人æ°ã®ããã€ã¡ãŒãžãç¹°ãè¿ãæœåºããéã®é 延ãšã³ã¹ããããã«åæžã§ããŸãã API Gatewayã§ã¯ãã¯ã©ã€ã¢ã³ããšïŒãã€ã¯ãïŒãµãŒãã¹éã®éæ¥ã¬ãã«ãå®è£ ããããšã«ãããããŒãžã§ã³ç®¡çãšãã©ã€ããªããŒã¿ã®é 眮ïŒã¹ããŒãžã³ã°ïŒã«å¥ã ã®æé ã䜿çšããŠãããããç¬ç«ããŠéçºããããšãã§ããŸãã ãããŸã§ã®éãäž»ãªåé¡ã®è§£æ±ºã«çŠç¹ãåœãŠãŸããç»ååŠçãµãŒãã¹ãAPIãšããŠæäŸããããšã§ãã
ããã§ã¯ãAPIãäœæããŸãããã AWSã³ã³ãœãŒã«ã§ã[API Gateway]ãã¯ãªãã¯ãã[æ°ããAPI]ãéžæããŠãAPIã«ååãä»ãããã説æãè¿œå ããŸãã ãImageAPIããšããååãéžæããŸããã

次ã«ãæ°ããAPIã®ãªãœãŒã¹ãäœæãïŒãImageProcessingServiceããšåŒã³ãŸãïŒããã®äžã«POSTã¡ãœãããäœæããŸãã çµ±åã®ã¿ã€ããšããŠãLambdaé¢æ°ããéžæããç»ååŠçã®ãµãŒãã¹ãšããŠäœ¿çšããLambdaé¢æ°ã®ååãå ¥åããŸãã ãã¡ãœããèŠæ±ãæ§æã§ãèš±å¯ã¿ã€ããããªããã«èšå®ããŸãïŒã€ãŸãããããªãã¯ãšã³ããã€ã³ãã«ãªããŸãïŒã 以äžã§ãã

çµ±åããã¹ãããã«ã¯ãããã¹ãããã¿ã³ãã¯ãªãã¯ããŸãã

次ã«ããã¹ããã€ããŒã{ãæäœãïŒãpingã}ãæå®ããŸãã APIãLambdaé¢æ°ã«æ£åžžã«é¢é£ä»ããããšã瀺ããäºæ³ãããããã³ãã®çµæãååŸããå¿ èŠããããŸãã
泚ïŒä»¥äžã§ã¯ãããå®å šãªïŒããæ·±ãïŒãã¹ããè¡ããŸãããéåžžãpingãªã©ã®åçŽãªæäœã«é¢é£ä»ãããããããã¬ãã«ãªãœãŒã¹ãšããŠGETã¡ãœãããAPIã«è¿œå ãããšäŸ¿å©ã§ãã ããã«ãããã©ã®ãã©ãŠã¶ãŒããã§ããAPIãLambdaé¢æ°ã«æ£ããæ¥ç¶ãããŠããããšããã°ãã確èªã§ããŸãã ãã¢ã¢ããªã±ãŒã·ã§ã³ïŒããã³äžè¬çã«ïŒã§ã¯ãããã¯å¿ é ã§ã¯ãããŸãããããã®ææ³ã奜ãå¯èœæ§ã¯ååã«ãããŸãã
次ã«æ¥ããã®ïŒéçS3ã³ã³ãã³ãïŒã«ã€ããŠã¯ãCORSãã¢ã¯ãã£ãã«ããå¿ èŠããããŸãã ç°¡åã§ãããããã€ãã®ã¹ãããã§è¡ãããŸãã API GatewayããŒã ã¯ãã®ããã»ã¹ãåŒãç¶ãç°¡çŽ åãããããããã§ã®æ瀺ãããã«ç¹°ãè¿ããªãããã«ããããã«ïŒãŸããªãæéåãã«ãªãå¯èœæ§ããããŸãïŒã ããã¥ã¡ã³ããåç §ããŸãã
[ãã®APIããããã€]ãã¿ã³ãã¯ãªãã¯ããŸãã ããã§ããµã€ããäœæããæºåããã¹ãŠæŽããŸããïŒ
-, 2: Amazon S3
: Javascript- S3, :
var ENDPOINT = 'https://fuexvelc41.execute-api.us-east-1.amazonaws.com/prod/ImageProcessingService'; angular.module('app', ['ui.bootstrap']) .controller('MainController', ['$scope', '$http', function($scope, $http) { $scope.loading = false; $scope.image = { width: 100 }; $scope.ready = function() { $scope.loading = false; }; $scope.submit = function() { var fileCtrl = document.getElementById('image-file'); if (fileCtrl.files && fileCtrl.files[0]) { $scope.loading = true; var fr = new FileReader(); fr.onload = function(e) { $scope.image.base64Image = e.target.result.slice(e.target.result.indexOf(',') + 1); $scope.$apply(); document.getElementById('original-image').src = e.target.result; // ! $http.post(ENDPOINT, angular.extend($scope.image, { operation: 'resize', outputExtension: fileCtrl.value.split('.').pop() })) .then(function(response) { document.getElementById('processed-image').src = "data:image/png;base64," + response.data; }) .catch(console.log) .finally($scope.ready); }; fr.readAsDataURL(fileCtrl.files[0]); } }; }]);
HTML- ( ) :
<!DOCTYPE html> <html lang="en"> <head> <title>Image Processing Service</title> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.4/css/bootstrap.min.css"> <link rel="stylesheet" type="text/css" href="http://fonts.googleapis.com/css?family=Open+Sans:400,700"> <link rel="stylesheet" type="text/css" href="main.css"> </head> <body ng-app="app" ng-controller="MainController"> <div class="container"> <h1>Image Processing Service</h1> <div class="row"> <div class="col-md-4"> <form ng-submit="submit()"> <div class="form-group"> <label for="image-file">Image</label> <input id="image-file" type="file"> </div> <div class="form-group"> <label for="image-width">Width</label> <input id="image-width" class="form-control" type="number" ng-model="image.width" min="1" max="4096"> </div> <button type="submit" class="btn btn-primary"> <span class="glyphicon glyphicon-refresh" ng-if="loading"></span> Submit </button> </form> </div> <div class="col-md-8"> <accordion close-others="false"> <accordion-group heading="Original Image" is-open="true"> <img id="original-image" class="img-responsive"> </accordion-group> <accordion-group heading="Processed Image" is-open="true"> <img id="processed-image" class="img-responsive"> </accordion-group> </accordion> </div> </div> </div> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.15/angular.min.js"></script> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.13.3/ui-bootstrap.min.js"></script> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.13.3/ui-bootstrap-tpls.min.js"></script> <script type="text/javascript" src="main.js"></script> </body> </html>
, CSS:
body { font-family: 'Open Sans', sans-serif; padding-bottom: 15px; } a { cursor: pointer; } /** LOADER **/ .glyphicon-refresh { -animation: spin .7s infinite linear; -webkit-animation: spin .7s infinite linear; } @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } @-webkit-keyframes spin { from { -webkit-transform: rotate(0deg); } to { -webkit-transform: rotate(360deg); } }
⊠S3:

URL S3 , ., âhttp://image-processing-service.s3-website-us-east-1.amazonaws.com/â. URL â :

API Gateway Lambda- , URL. . , , !
, API. : « HTTPS-» Lambda, POST , API Gateway:
{ "options": { "host": "fuexvelc41.execute-api.us-east-1.amazonaws.com", "path": "/prod/ImageProcessingService", "method": "POST" }, "data": { "operation": "getSample" } }
. : Lambda- Amazon DynamoDB. Lambda, « »:
{ "operation": "unit", "function": "HTTPSInvoker", "resultsTable": "unit-test-results", "testId": "LinuxConDemo", "event": { "options": { "host": "fuexvelc41.execute-api.us-east-1.amazonaws.com", "path": "/prod/ImageProcessingService", "method": "POST" }, "data": { "operation": "getSample" } } }
, , . Lambda, « »:
{ "operation": "load", "iterations": 100, "function": "TestHarness", "event": { "operation": "unit", "function": "HTTPSInvoker", "resultsTable": "unit-test-results", "testId": "LinuxConLoadTestDemo", "event": { "options": { "host": "fuexvelc41.execute-api.us-east-1.amazonaws.com", "path": "/prod/ImageProcessingService", "method": "POST" }, "data": { "operation": "getSample" } } } }
:

, , .. -, API Gateway HTTP, . , - DynamoDB, .
ãŸãšã
, , . «» , , , - : , , API, . !