I'm just started with Sharp, and trying to overlay two images in the nodejs server and output a png, both are web images. So I'm trying with (picked random img urls here)
const server = http.createServer(async (req, res) => {
function processImg(){
let buffer = sharp('https://pbs.twimg.com/profile_images/3572978953/8c522d3ea384cd42e46a4a2498300c35_400x400.jpeg')
.composite({input: 'https://abs.twimg.com/a/1569889127/img/delight/delight_prompt_3.png'})
.png()
.toBuffer()
.catch(err=>{console.log(err)})
return buffer
}
let newBuffer = await processImg();
res.setHeader("Content-Type", "image/png");
res.end(newBuffer);
}
Then I get error [Error: Input file is missing]. Does it mean the image src was not working?
There's no support for remote URL based input so you'll need to use an intermediate module to handle the networking side of things - see #930 for an example of this.
https://sharp.pixelplumbing.com/en/stable/api-constructor/#parameters
(I've added commit e627f6d to help clarify this in the docs.)
As @lovell mentioned, Sharp does not support remote URL as Input.
Anyway, There's another way to achieve your goal - Download Image manually & Use Buffer as Input:
import axios from "axios";
const input = (await axios({ url: "https://somewhere.com/some-image.png", responseType: "arraybuffer" })).data as Buffer;
const composite = (await axios({ url: "https://somewhere.com/another-image.png", responseType: "arraybuffer" })).data as Buffer;
const output = await sharp(input).composite({ input: composite }).png().toBuffer();
Unfortunately. request does not support Promise interface. You'll have to wrap request calls to your own Promise instance:
import * as request from "request";
const getBody = (url: string): Promise<Buffer> => new Promise<Buffer>)((resolve, reject) => {
// `encoding: null` is important - otherwise you'll get non-buffer response body
request({ url, encoding: null }, (e, res, body) => {
if (e) { return reject(e); }
else if (200 <= res.statusCode && res.statusCode < 300) {
return resolve(body);
} else {
return reject(new Error(`Unexpected response status ${res.statusCode}`));
}
});
});
const input = await getBody("https://some-where.com/some-image.png");
const composite = await getBody("https://another-server.com/another-image.png");
const output = await sharp(input).composite({ input: composite }).png().toBuffer();
Anyway, Based on your code example, It's not good to production usage because someone calls your server, every time server should do expensive tasks (download image from remote server and transform image - imagine If someone calls your server 100 times concurrently - your server will be unavailable.)
To optimize performance issue, You should consider caching.
If composite image is always same, you can store composite image to memory and re-use them:
import axios from "axios";
import * as sharp from "sharp";
let cachedCompositeInput = null;
async function compositeImage(inputUrl: string): Promise<Buffer> {
if (!cachedCompositeInput) {
cachedCompositeInput = await getCompositeImageSomehow();
}
const input = (await axios({ url: remoteUrl, responseType: "arraybuffer" })).data;
return await sharp(input).composite({ input: cachedCompositeInput }).png().toBuffer();
}
Anyway, Upper example can reduce number of downloading composite image. but image composition is still required.
To reduce image composition tasks. You can setup your own CDN in front of your server.
See https://github.com/donnemartin/system-design-primer#content-delivery-network for details.
Thank you @lovell , the example helped a lot! Able to resolve the problem.
Thanks @mooyoul for the great thorough solution! I like the idea of caching the composite image (very close to the actual problem I'm dealing with).
Most helpful comment
As @lovell mentioned, Sharp does not support remote URL as Input.
Anyway, There's another way to achieve your goal - Download Image manually & Use Buffer as Input:
using Axios
using Request
Unfortunately. request does not support Promise interface. You'll have to wrap request calls to your own Promise instance:
Anyway, Based on your code example, It's not good to production usage because someone calls your server, every time server should do expensive tasks (download image from remote server and transform image - imagine If someone calls your server 100 times concurrently - your server will be unavailable.)
To optimize performance issue, You should consider caching.
If composite image is always same, you can store composite image to memory and re-use them:
Anyway, Upper example can reduce number of downloading composite image. but image composition is still required.
To reduce image composition tasks. You can setup your own CDN in front of your server.
See https://github.com/donnemartin/system-design-primer#content-delivery-network for details.