Life can be hard sometimes, especially for a developer. Our journeys from A to B often take us via C, D and Z. This clip from Malcolm in the Middle illustrates this quite well.
To get the point of this post – the other day I needed to to make a microservice for taking screenshots of web pages. Just a simple endpoint that would accept a URL and return a screenshot of it.
AWS Lambdas are perfect for this: no servers to run, and just pay per invocation (i.e. per screenshot).
There seemed to be tons of example code to do this, but none of it worked quite right, or there were other problems to deal with:
- Lambda deployment package size must be < 50MB, so you might have to upload Chromium to S3 and pull from there at runtime – another moving part to manage
- Bugs in the latest versions of packages
- The example code had screenshots upload to random services or saved to S3
In short, it got overly complicated.
So my pain is your gain, and having solved these problems I share the solution with you.
lambda-browser-screenshots
The code is available on GitHub. It’s a single-file NodeJS script that builds to a zip that’s only about 42MB, including dependencies. You can easily upload this as a Lambda package without having to pull binaries out of S3.
Building
Build instructions are included in the readme, but essentially, after cloning the repository:
- Run
npm install
inside thesrc
directory. - Zip the
src
directory, which should now containnode_modules
. - There's no Step 3.
These steps can also be run just by using make
.
AWS Setup
Now that you have it built, you can set up your Lambda and upload the zip. I’ll assume you have some prior Lambda knowledge, but the basic setup is:
- Create a new Node.js 12.x Lambda Function
- Add a lambda trigger of type API Gateway
- Set API type to REST API. Configure security and so on.
- Under additional settings, make sure to add image/png to Binary media types, otherwise your screenshots come back as base64 text
Now you can upload the zip file you built previously as the Lambda’s Function code. Although it suggests For files larger than 10 MB, consider uploading using Amazon S3, you’ll probably be fine through the web interface (I have been).
The time it takes to render and screenshot the page (and therefore the overall execution time) depends on how much RAM is allocated to the Lambda function. About 1600MB seems to be a good amount to get the screenshot back within 2-3 seconds. Thus depending on your AWS region you should be able to get 1,000,000 screenshots for around $60 USD.
Usage
Simply make a request to your Lambda URL, and pass the URL you want to screenshot as the url
query parameter. You can also optionally specify parameters width
and/or height
of the browser window. By default these are 1280
and 800
, respectively.
Your request must specify the header Accept: image/png
. Without it you will receive the image data base 64 encoded, instead of in binary format.
Here’s how to screenshot this very post, using curl
:
$ curl -H "Accept: image/png" \
https://functionname.execute-api.region.amazonaws.com/default/browser-screenshots\
\?url\=https://terashift.co.nz/browser-screenshot-microservice-aws-lambda/\
\&width\=1460\&height\=820 \
> browser-screenshot-inception.png
And here is the resulting screenshot.
Conclusion
I hope that you someone finds this tool useful, as a serverless screenshot microservice. Once built and uploaded it should continue to just work without requiring any maintenance.
If you decide you need more features or find that you can upgrade the dependencies to newer versions, while staying under than 50MB limit, please contribute back with a pull request so that rest of the community can benefit as well.