如何利用 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-project,你在 main 分支上有一個 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
(async function main() {
2
await compressAndCopyImages(imagesDir, targetDir);
3
await gitOperations();
4
await updateImagePaths();
5
})();

以上代碼實現了圖片的壓縮、搬遷、上傳及路徑替換,完成了從本地圖片到 CDN 託管的自動化過程。這樣,在本地開發時使用本地圖片,生產環境中則使用 CDN 加速的圖片,提高了網站的加載速度和性能。