使用github pages做爲圖片靜態服務器

在一些包含服務器的我的項目上,圖片等靜態資源通常有兩種處理方式:直接使用業務服務器處理和存儲,或者交給專門的圖片服務器去處理和存儲。這兩種方式都須要服務相對穩定和長效,否則的話總要面對數據的遷移和維護,這是一項成本,不當心的時候還會有坑存在。然而有些時候,好比咱們的我的博客,或者用於演示的一些demo中的圖片,必定須要圖片服務器嗎?對於這種需求量級並不大的靜態資源存儲,咱們是否能夠直接用本身的github做爲服務器,若是能夠,對我的項目來講這個靜態資源服務器將會是很是穩定的,除非存儲量達到了你的github空間上限或者由於某些緣由github再也不能被人們訪問。javascript

分析:

如何使用github做爲靜態資源服務器呢,很簡單,只要實現如下三點就能夠:
1. 捕獲客戶端的上傳資源請求並處理
2. 關聯github.io本地倉庫,將上一步的處理結果同步到這個本地倉庫
3. 遠程推送,返回客戶端可訪問的靜態資源連接java

除了以上三點,還須要一個配置項來關聯。這裏實現一個基於koa的使用github.io做爲圖片靜態服務器的中間件,按照上面的分析思路,實現以下:node

配置文件

下面是指望的用法,經過將配置項傳入githubAsImageServer獲得中間件,並掛載到koa實例上來實現上述功能。git

const Koa = require('koa')
const app = new Koa()

const githubAsImageServer = require('github-as-image-server');

app.use(githubAsImageServer({
  targetDir: 'D:/project/silentport.github.io', // github.io倉庫的本地地址
  repo: 'https://github.com/silentport/silentport.github.io.git', // github.io倉庫的遠程地址
  url: 'https://silentport.github.io', // 你的github.io域名
  dir: 'upload', // 圖片文件的上傳目錄
  project: 'blog', // 項目名,用於指定一個上傳子目錄
  router: '/upload' // 請求路由,請求非該指定路由時中間件將跳過
}))


app.listen(8002, () => {
  console.log('server is started!');
})

複製代碼

githubAsImageServer

很明顯,githubAsImageServer這個函數是實現一切的邏輯所在,代碼結構以下:github

module.exports = options => async (ctx, next) => {

    // 非上傳請求直接跳過
    if (ctx.method !== 'POST' || ctx.path !== options.router) {
      next();
      return;
    }

    let result = null; // 最終響應到客戶端的值
    const { targetDir, repo, url, dir, project } = options;
    const uploadDir = targetDir + '/' + dir || 'upload'; // 上傳目錄
    const childDir = uploadDir + '/' + project;  // 上傳子目錄
    const form = new formidable.IncomingForm();
   
    // 在指定目錄下執行shell命令
    const execCommand = async (command, options = { cwd: targetDir }) => {
      const ls = await exec(command, options);
      console.log(ls.stdout);
      console.log(ls.stderr);
    };

    const isExistDir = dir => fs.existsSync(dir);
    
    // 確保文件夾存在
    const ensureDirExist = dir => {
      if (!isExistDir(dir)) fs.mkdirSync(dir);
    }
    
   // 遠程推送
    const pushToGithub = async imgList => {
      await execCommand('git pull');
      await execCommand('git add .');
      await execCommand(`git commit -m "add ${imgList}"`);
      await execCommand('git push');
    }
    
    // 解析上傳請求後的回調
    const callback = async (files, keys) => {
      let result = { url: [] };
      let imgList = [];
      await (async () => { 
        for await (const key of keys) {
          const originPath = files[key].path;
          const targetPath = uniquePath(path.join(path.dirname(originPath), encodeURI(files[key].name)));
          const imgName = targetPath.split(/\/|\\/).pop();
          const webpName = imgName.split('.')[0] + '.webp';
          const resUrl = url + '/upload/' + project + '/' + webpName;
          const newPath = targetPath.replace(new RegExp(imgName), webpName);
          fs.renameSync(originPath, targetPath);
          try {
            // 將圖片轉爲webp格式,節省服務空間
            await convertToWebp(targetPath, newPath);
          } catch (err) {
            next();
            return;
          }   
          imgList.push(webpName);
          result.url.push(resUrl);

        }   
      })();
      await pushToGithub(imgList.toString());
      return result;
    }
    
    // 文件名統一加上生成時間
    const uniquePath = path => {
      return path.replace(
        /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        suffix => `_${getDate()}${suffix}`
      );
    }
    // 本地github.io倉庫不存在時先clone
    if (!isExistDir(targetDir)) {
      ensureDirExist(targetDir);
      const cwd = targetDir.split('/');
      cwd.pop();
      await execCommand(`git clone ${repo}`, {
        cwd: cwd.join('/')
      });

    }

    ensureDirExist(uploadDir);
    ensureDirExist(childDir)

    form.uploadDir = childDir;
    
    try {
      result = await formHandler(form, ctx.req, callback, next);
    } catch (err) {   
      result = {
        url: []
      }
    }
    ctx.body = result
  };

複製代碼

爲了處理多圖片上傳請求的狀況,待處理完畢後再統一返回客戶端可訪問的圖片連接列表,這裏用到了for await of異步遍歷,此語法只支持node 10.x以上的版本。此外,爲了節省服務空間,將全部圖片轉爲了webp格式。web

完整代碼參見github.com/silentport/…,若是你感興趣,歡迎與我討論或者提issue。shell

相關文章
相關標籤/搜索