🖼️

SvelteKit + ipx + unpicで動的で最適な画像配信

2023/12/07に公開

はじめに

こんにちは、株式会社Liquitousのエンジニアの西村と申します。

本日はSvelte Advent Calendar 2023の7日目ということで、ipxとUnpicを用いた、SvelteKitアプリケーションから呼び出す画像の動的な最適化方法について解説します。

SvelteKitのセットアップ

今回の検証はNode.js 20.10.0、pnpm 8.11.0を使用しています。

  1. プロジェクトの作成
npm create svelte@latest sveltekit-ipx-unpic
設定(今回は何でも動くと思いますが)

Skeletonプロジェクトを選択し、TypeScriptを使用します。オプションはESLint, Prettierを選択しました。

create-svelte version 5.3.3

┌  Welcome to SvelteKit!
│
◇  Which Svelte app template?
│  Skeleton project
│
◇  Add type checking with TypeScript?
│  Yes, using TypeScript syntax
│
◇  Select additional options (use arrow keys/space bar)
│  Add ESLint for code linting, Add Prettier for code formatting, Try the Svelte 5 preview (unstable!)
│
└  Your project is ready!

次に、作成したプロジェクトのディレクトリに移動します。

cd sveltekit-ipx-unpic
  1. アダプターをNodeに切り替える
pnpm add @sveltejs/adapter-node -D
svelte.config.js
+import adapter from '@sveltejs/adapter-node';
-import adapter from '@sveltejs/adapter-auto';
  1. Expressサーバーを準備し、その上でSvelteKitを動かす
pnpm add express
server.js
import { handler } from './build/handler.js';
import express from 'express';

const app = express();

app.use(handler);

app.listen(3000, () => {
  console.log('listening on port 3000');
});
package.json
"start": "node ./server.js",
  1. ビルドし、動作を確認する
pnpm build
pnpm start

これでポート3000でSvelteKitが起動します。

ipxの紹介

ipxは、sharpsvgoを使って画像の最適化を簡単に行うことができます。
UnJSというNuxtのチームが開発しているプロジェクトの一部で、Nuxt ImageやNetlifyでも使用されています。
https://github.com/unjs/ipx

ipxは単体でサーバーとして立ち上げることも可能ですが、今回はExpressのミドルウェアとして使用します。

ipxをExpressのミドルウェアとして設定

pnpm add ipx
ipxServer.js
import { createIPX, ipxHttpStorage, createIPXNodeServer } from 'ipx';

// 今回は例にpicsum.photosを使っていますが、実際には自分が提供する画像のホストを指定します。
const imageHost = "picsum.photos";
const storage = ipxHttpStorage({
	domains: [imageHost]
});

const ipx = createIPX({
  storage,
	httpStorage: storage,
	alias: {
		'/image': `https://${imageHost}`
	},
});

export default createIPXNodeServer(ipx);
server.js
import { handler } from './build/handler.js';
import express from 'express';
+import ipxServer from './ipxServer.js'

const app = express();

+// パスの先頭が/_ipxの場合にIPXの処理を行うようにします。
+app.use('/_ipx', createIPXNodeServer(ipx));

app.use(handler);

app.listen(3000, () => {
  console.log('listening on port 3000');
});

ipxをViteのプラグインとしても設定

Expressのミドルウェアのままだと開発サーバーではipxを使えないため、Viteのプラグインとしても動作するように設定します。

vite.config.ts
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
+import ipxServer from './ipxServer';
+import type { Plugin, ViteDevServer } from 'vite';
+
+const ipxPlugin: Plugin = {
+	name: "ipx",
+	apply: "serve",  // 開発時のみ有効
+	configureServer(server: ViteDevServer) {
+		server.middlewares.use((req, res, next) => {
+			// eslint-disable-next-line @typescript-eslint/ban-ts-comment
+			// @ts-ignore
+			if (req.url.startsWith('/_ipx')) {
+				// eslint-disable-next-line @typescript-eslint/ban-ts-comment
+				// @ts-ignore
+				req.url = req.url.replace('/_ipx', '');
+				return ipxServer(req, res);
+			}
+			next();
+		})
+	},
+}

export default defineConfig({
	plugins: [
		sveltekit(),
+		ipxPlugin
	]
});

動作確認

それでは、一度サーバーを立ち上げてみましょう。

pnpm dev
  • 例えば、https://picsum.photos/id/20/3670/2462の画像をipxで縦横400pxにリサイズして取得するには、ブラウザで次のURLにアクセスします。
    http://localhost:5173/_ipx/s_400x400/https://picsum.photos/id/20/3670/2462

  • エイリアスを設定しているため、次のURLにアクセスしても同様の結果を得られます。
    http://localhost:5173/_ipx/s_400x400/image/id/20/3670/2462

  • リサイズに合わせて画像のフォーマット変換やブラー処理などを入れるには、カンマ区切りで修飾子を追加します。
    http://localhost:5173/_ipx/s_400x400,f_webp,blur_5/image/id/20/3670/2462

その他のオプションについては以下のリンクから確認できます。
https://github.com/unjs/ipx#modifiers

これで簡単に画像を変換することはできましたが、毎回オプションを見ながらURLをいじるのは面倒です。そこで、もう1つライブラリを紹介します。

Unpicの紹介

unpic-imgは、様々な画像CDNから提供される画像変換APIを、サービス間の差を意識せずに、一貫したインターフェースを通して扱えるようにするライブラリです。
CloudinaryやImgixをはじめ多くのCDNに対応している他、上記でご紹介したipxもサポートしています。
このセクションでは、unpic-imgのSvelte用コンポーネントである@unpic/svelteを使っていきます。

@unpic/svelteの導入

pnpm add @unpic/svelte
src/+routes/+page.svelte
<script lang="ts">
  import { Image } from "@unpic/svelte";
</script>

<Image
  src="/image/id/20/3670/2462"
  layout="constrained"
  width={800}
  height={600}
  alt="デザイナーのおしゃれな机"
/>

これでhttp://localhost:5173にアクセスすると画像が一枚表示されます。この時画像のURLを確認すると、http://localhost:5173/_ipx/s_640x480/image/id/20/3670/2462のようにオプションや_ipxといったprefixが追加されているのが分かります。

unpicコンポーネントの詳細な使い方についてはまた後日まとめます。一旦はここ読んどくとokってページを貼っておきます。
https://unpic.pics/img/learn/
https://unpic.pics/img/svelte/

終わりに

以上が、動的な画像を配信してSvelteで表示する仕組みの作り方です。
また、本プロジェクトのレポジトリはこちらですので良ければご参考ください。
https://github.com/KazuumiN/sveltekit-ipx-unpic

最後に告知です。12月8日にSvelte Japan Online Meetup #1が開催されます!
オンラインからご視聴いただけるのでぜひご登録ください。
私も、SvelteKitをプロダクション投入した話について発表する予定です📣
https://svelte-jp.connpass.com/event/297474/

株式会社Liquitous

Discussion