はじめに
placekittenとかplaceholdみたいなダミー画像生成サービスを作りたいと思ったのでちょっと試してみた記録です。
正直秒で作れたので特に言うことはないです。
完成形イメージ
それじゃ実装を説明します。
実装
事前準備
まずは適当なフォルダ作ってその中で、下記のような形で必要なライブラリをインストールしましょう。
npm init -y
npm install express cors sharp
※sharp
は画像リサイズ用のライブラリです(https://sharp.pixelplumbing.com/)
ファイル構成は下記のような形にします。index.html
は中身適当でいいですが、下記に一応自分のを置いときます。
私の画像はwaifu diffusionで生成したものを利用していますが、何でもOKです。(jsのサイズ指定部分を合わせて変更して下さい。)
.
├── images
│ ├── 1.png
│ ├── 2.png
│ ├── 3.png
│ ├── 4.png
│ ├── 5.png
│ ├── 6.png
│ ├── 7.png
│ ├── 8.png
│ ├── 9.png
│ └── 10.png
├── index.html
├── index.js
├── node_modules
├── package-lock.json
├── package.json
└── readme.md
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<title>placeImage</title>
</head>
<body>
<h1>placeImage</h1>
<img src="http://localhost:3000/400/300" />
<h2>使い方</h2>
<p>下記のように幅と高さを指定すれば画像を取得できます。</p>
<p class="caution">
注意:幅512px、高さ512px以上の指定は、512pxにそれぞれリサイズされますのでご注意下さい。<br />
また、3桁以上の数字を入力した場合や数値以外の値を入力した場合は404エラーとなります。
</p>
<p class="link">
例:<a href="http://localhost:3000/400/300" target="_blank"
>http://localhost:3000/400/300</a
>
</p>
</body>
</html>
あと、package.json
にindex.jsを起動するための記述を追加して下さい。
"scripts": {
"start": "node index.js"
}
apiの実装
コードの全体像が下記です。先程作成したindex.js
内に記述して下さい。
const express = require("express");
const path = require("path");
const cors = require("cors");
const sharp = require("sharp");
const app = express();
// 画像の数を入れる定数
const imagesLength = 10;
// 他のサービスで利用する場合にCORSエラーが起きないようにするやつ(ローカルだけならいらない)
app.use(cors());
// indexページの生成
app.get("/", (req, res) => {
res.sendFile(__dirname + "/index.html");
});
// widthとheightを1~4桁で取ってランダムな画像をトリミングして表示
app.get("/:width([0-9]{1,3})/:height([0-9]{1,3})", async (req, res) => {
const width = Math.min(Number(req.params.width), 512);
const height = Math.min(Number(req.params.height), 512);
let fileName = Math.floor(Math.random() * imagesLength) + 1 + ".png";
let filepath = path.join(__dirname, `images/${fileName}`);
try {
const resizedBuffer = await sharp(filepath)
.resize(width, height, { fit: "cover" })
.toBuffer();
const encodedBuffer = resizedBuffer.toString("base64");
var img = Buffer.from(encodedBuffer, "base64");
res.writeHead(200, {
"Content-Type": "image/png",
"Content-Length": img.length,
});
res.end(img);
} catch (e) {
res.status(404).send("<h1>404 page not found</h1>");
}
});
// 404ページ
app.all("*", (req, res) => {
res.status(404).send("<h1>404 page not found</h1>");
});
const port = process.env.PORT || 3000;
app.listen(port, () => console.log("listen on port:", port));
重要な画像の取得部分のみ説明します。
// widthとheightを1~4桁で取ってランダムな画像をトリミングして表示
app.get("/:width([0-9]{1,3})/:height([0-9]{1,3})", async (req, res) => {
const width = Math.min(Number(req.params.width), 512);
const height = Math.min(Number(req.params.height), 512);
let fileName = Math.floor(Math.random() * imagesLength) + 1 + ".png";
let filepath = path.join(__dirname, `images/${fileName}`);
try {
const resizedBuffer = await sharp(filepath)
.resize(width, height, { fit: "cover" })
.toBuffer();
const encodedBuffer = resizedBuffer.toString("base64");
var img = Buffer.from(encodedBuffer, "base64");
res.writeHead(200, {
"Content-Type": "image/png",
"Content-Length": img.length,
});
res.end(img);
} catch (e) {
res.status(404).send("<h1>404 page not found</h1>");
}
});
expressではパスに正規表現を利用できるため、下記のような形で横幅と高さのパラメータを制限しています。
今回は数値かつ、1~3桁の範囲で指定しています。
"/:width([0-9]{1,3})/:height([0-9]{1,3})"
下記で横幅、高さを指定します。今回は最大値を512にしていますが、好きに変更して下さい。
const width = Math.min(Number(req.params.width), 512);
const height = Math.min(Number(req.params.height), 512);
ここで画像ファイルをランダムに取得しています。
let fileName = Math.floor(Math.random() * imagesLength) + 1 + ".png";
let filepath = path.join(__dirname, `images/${fileName}`);
最後に画像をバッファにしてレスポンスに渡しています。
何らかの理由で画像が表示できなければ404ページを表示します。
try {
const resizedBuffer = await sharp(filepath)
.resize(width, height, { fit: "cover" })
.toBuffer();
const encodedBuffer = resizedBuffer.toString("base64");
var img = Buffer.from(encodedBuffer, "base64");
res.writeHead(200, {
"Content-Type": "image/png",
"Content-Length": img.length,
});
res.end(img);
} catch (e) {
res.status(404).send("<h1>404 page not found</h1>");
}
以上です。
それでは〜〜〜
p.s.
1年前に転職してから一個も記事あげられてなかったのですが、ようやく記事を書けました...