jsdelivr無料CDNを利用してスクリプトで自動アップロードとローカル画像パスの置換を実現する方法

生産環境では、画像をCDN(コンテンツ配信ネットワーク)に保存することで、読み込み速度を向上させ、ユーザー体験を向上させることができます。最近、次のような要件に直面しました。プロジェクトでローカル画像を参照する場合、/images/xxx.{jpg,jpeg,png,gif}などを使用してローカル開発を容易にし、開発が完了したら、imagesディレクトリのすべての画像を圧縮して /project/resources/imagesに移動(resourcesはgitプロジェクト)し、gitを介して自動的にGitHubにアップロードし、ローカルプロジェクトで参照されている画像パスを見つけ、CDNパスに変更します。このプロセスを理解した上で、スクリプトを使用してこのプロセスを自動化できます。まず、jsdelivrがCDNをどのように実現するかを理解しましょう。

jsdelivr CDN と GitHub の関係

jsdelivrは、無料で信頼性の高いコンテンツ配信ネットワーク(CDN)サービスで、開発者が静的リソースをGitHubリポジトリに保存し、jsdelivrを介してグローバルに加速アクセスを行うことを許可します。具体的には、jsdelivrはCDNアドレスを提供し、GitHubリポジトリに保存されているリソースを加速することができ、これによりこれらのリソースがグローバルに迅速に読み込まれるようになります。

GitHubプロジェクトとjsdelivrを関連付ける方法

  1. リソースをGitHubリポジトリにアップロードする
    まず、開発者は静的リソース(画像、JavaScriptファイル、CSSファイルなど)をGitHubリポジトリの特定のディレクトリにアップロードする必要があります。例えば、my-projectという名前のGitHubリポジトリがあり、その中に画像を保存するimagesフォルダがあるとします。

  2. jsdelivr CDNを使用してアクセスする
    一旦リソースがGitHubリポジトリにアップロードされると、jsdelivrが提供するURLを使用してこれらのリソースにアクセスできます。jsdelivrはGitHubプロジェクトに関連付けられたCDNアドレスを提供し、そのアドレスは以下の形式に従います:

    1
    https://cdn.jsdelivr.net/gh/[GitHubユーザー名]/[リポジトリ名]@[タグまたはブランチ]/[ファイルパス]
    • [GitHubユーザー名]:あなたのGitHubユーザー名。
    • [リポジトリ名]:リソースをアップロードしたGitHubリポジトリ名。
    • [タグまたはブランチ]:使用するGitHubのタグまたはブランチ(例:mainv1.0)。
    • [ファイルパス]:リポジトリ内のリソースのパス。

    あなたのGitHubユーザー名がjohnsmith、リポジトリ名がmy-projectmainブランチにimagesフォルダがあり、その中にlogo.pngという画像があるとします。この画像にアクセスするために、次のjsdelivr CDN URLを使用できます:

    1
    https://cdn.jsdelivr.net/gh/johnsmith/my-project@main/images/logo.png

    ウェブページでこのURLを使用して画像を参照すると、jsdelivrがグローバルのサーバーキャッシュからこの画像を加速読み込みし、読み込み速度を向上させます。

  3. 自動同期更新
    GitHubリポジトリのリソースを更新すると、jsdelivrは自動的にGitHubから最新のリソースを取得し、CDN上のリソースを最新の状態に保ちます。つまり、GitHubリポジトリに新しいバージョンのリソースをアップロードするだけで、jsdelivrのURLが自動的に更新され、最新のファイルを提供するようになります。

このようにして、開発者はjsdelivr CDNのグローバル分散ネットワークを利用して、GitHubリポジトリに保存された静的リソースの読み込み速度を向上させ、ユーザー体験を向上させることができます。

実現ステップ

1. 依存関係のインストール

Terminal window
1
pnpm add sharp fs-extra globby simple-git replace-in-file;

2. 画像の圧縮と移動

まず、sharpライブラリを使用して画像を圧縮し、フォルダ構造を保持しつつ画像をターゲットディレクトリに移動します。以下は実装コードです:

1
import { promises as fs } from "fs";
2
import path from "path";
3
import sharp from "sharp";
4
import fse from "fs-extra";
5
import { globby } from "globby";
6
7
const imagesDir = "/project/myproject/public/images";
8
const targetDir = "/project/resources/images";
9
10
async function compressAndCopyImages(srcDir, destDir) {
11
try {
12
const sourDir = `${srcDir}/**/*.{jpg,jpeg,png,gif}`;
13
await fse.ensureDir(destDir);
14
const entries = await globby([sourDir], { onlyFiles: false });
15
16
for (const entry of entries) {
17
const relativePath = path.relative(srcDir, entry);
18
const destPath = path.join(destDir, relativePath);
19
20
if ((await fs.stat(entry)).isDirectory()) {
21
await fse.ensureDir(destPath);
22
} else {
23
const metadata = await sharp(entry).metadata();
24
25
let options = {};
26
let formatOptions = {};
27
28
switch (metadata.format) {
29
case "gif":
30
options = { animated: true, limitInputPixels: false };
31
formatOptions = { colours: 128 };
32
break;
33
default:
34
formatOptions = { quality: 75 };
35
}
36
37
if (metadata.size < 10000) {
38
await fse.copy(entry, destPath);
39
console.log(`Copied ${relativePath} without compression`);
40
} else {
41
const dirPath = path.dirname(destPath);
42
await fse.ensureDir(dirPath);
43
await sharp(entry)?.[metadata.format](formatOptions).toFile(destPath);
44
console.log(`Compressed and copied ${relativePath}`);
45
}
46
}
47
}
48
} catch (error) {
49
console.error("Error during image compression and copy:", error);
50
}
51
}

3. Git操作

次に、simple-gitライブラリを使用して画像を自動的にGitHubリポジトリにアップロードします:

1
import simpleGit from "simple-git";
2
3
const gitRepoDir = "/project/resources";
4
5
async function gitOperations() {
6
try {
7
const git = simpleGit(gitRepoDir);
8
await git.add("./*");
9
await git.commit("Update images");
10
await git.push("origin", "main");
11
console.log("Pushed changes to GitHub");
12
} catch (error) {
13
console.error("Error during Git operations:", error);
14
}
15
}

4. 画像パスの更新

最後に、replace-in-fileライブラリを使用してプロジェクト内のローカル画像パスをオンラインパスに置き換えます:

1
import { globby } from "globby";
2
import { replaceInFile } from "replace-in-file";
3
4
// 画像パスを置き換えるディレクトリとファイル
5
const contentDir = "/project/myproject/src/content/**/*.{html,js,jsx,ts,tsx}";
6
const cdnBaseUrl =
7
"https://cdn.jsdelivr.net/gh/[GitHubユーザー名]/resources/images";
8
9
async function updateImagePaths() {
10
try {
11
const files = await globby([contentDir]);
12
13
const replaceOptions = {
14
files,
15
from: /(["'])\/images\/(.+\.(jpg|jpeg|png|gif))/g,
16
to: `$1${cdnBaseUrl}/$2`,
17
};
18
19
const results = await replaceInFile(replaceOptions);
20
console.log(
21
"Modified files:",
22
results
23
.filter((result) => result.hasChanged)
24
.map((result) => result.file),
25
);
26
} catch (error) {
27
console.error("Error during updating image paths:", error);
28
}
29
}

5. メイン関数

上記のステップを1つのメイン関数に統合します:

1
(async function main() {
2
await compressAndCopyImages(imagesDir, targetDir);
3
await gitOperations();
4
await updateImagePaths();
5
})();

以上のコードは、画像の圧縮、移動、アップロード、およびパスの置換を実現し、ローカル画像からCDNホスティングへの自動化プロセスを完了します。このようにして、ローカル開発中にローカル画像を使用し、生産環境ではCDN加速された画像を使用することで、ウェブサイトの読み込み速度とパフォーマンスを向上させます。