辛宝Otto

辛宝Otto 的玄酒清谈

北漂前端程序员儿 / 探索新事物 / Web Worker 主播之一/内向话痨
xiaoyuzhou
email

使用 Nitro + Serverless 函數實現動態頭圖 Banner

image.png

關聯內部鏈接:

  • [[速通 - unjs 家族的 Web 框架 Nitro]]
  • [[速通 - 騰訊雲雲函數 scf]]
  • [[編寫一個小工具方便快速為文章添加 Banner]]
  • [[速通 - 動態 SVG 圖片生成方案 Satori]]

背景#

現在比較習慣在文章頂部增加一張包含當前標題的圖片,在不同平台會默認第一張圖片有用,比如認為這是文章 Banner、Cover、Hero Image 等說法。

之前在 [[編寫一個小工具方便快速為文章添加 Banner]] 文章中就提到了我寫了一個網站,可以在頁面上操作生成 banner。

那有沒有一種辦法直接寫一個服務,當我訪問 xx.com/cover?title=xxx 時候自動返回一張圖片,這樣的話,我就不需要打開頁面去創建了。後續我只需要查看日誌就知道有多少人訪問過啦!

而且不同於瀏覽器,在 node.js 裡渲染圖片要更複雜一點,不可以像瀏覽器那樣把計算、渲染的能力丟給瀏覽器了,最直接的就是 html+css 生成截圖不可以了,因為 node 環境裡沒有 css 渲染的能力,得靠 puppeteer 那一套,得使用 canvas/svg 等方案來畫。

Canvas 的跨平台兼容就顯現出來了。但 Canvas 沒接觸過不太會,得現學。後來發現 svg 也非常簡單,而且返回的是字符串,體積更小。

魚和熊掌不可兼得啊。

需求設計#

首先這是一個 node 服務,我們攜帶參數請求接口,返回的是一張圖片。這就需要我們部署後端。

相通的請求參數應該出現一致的結果,主要是背景圖,不要一刷新頁面裡的圖片變了,產生不必要的誤會。

後續可以增加不同的模板,展望可以做成服務對外提供。

參數我們希望:

  • width 寬度
  • height 可選,自動高度
  • 比例 4:3 16:9 21:9 2:1 1:1 等
  • title 文字內容
  • source 角標,比如 @ijust.cc
  • 指定色相 0-360
  • 默認開啟漸變 linear-gradient

這樣基本就和網頁版 https://ijust.cc/tools/canvas 一致了。

技術設計#

交付托管#

node 服務托管,顯然是可以使用 serverless,我們可以選擇國內的雲廠商服務,也可以使用 cloudflare/vercel 提供的服務。考慮服務為了正常需要遷移,可以選擇 serverless 框架,之前提到過的 [[速通 - unjs 家族的 Web 框架 Nitro]] 就可以派上用場了。Nitro 可以很容易構建出不同的產物。

雲廠商這裡我們使用騰訊雲 Serverless 函數,後面補充 Cloudflare Workers。

主題色持久化#

相通的內容產生相通的結果,設計一個方案,根據 title 內容算出一個數字,表達主色調。問了 GPT 給了幾個方案,比如每個字符串去算 charCodeAt,得到的數字進行處理。

function stringToDegrees(inputString) {
    let hash = 0;
    for (let i = 0; i < inputString.length; i++) {
        let char = inputString.charCodeAt(i);
        hash = ((hash << 5) - hash) + char;
        hash |= 0; // 將哈希值轉換為32位整數
    }
    return Math.abs(hash % 360);
}
stringToDegrees('中文') // 194

使用 Satori 製作動態 SVG#

畫布因為考慮 Canvas,很多東西就不能復用了,經過實踐發現 Vercel 的 Satori最近比較火熱,在 [[12 - 速通 syntax.fm 659 OG Image]] 這一期其實也提到過。寫 demo 成功了,以後能經常用得上。

image.svg

這麼一張 SVG 圖,才 4.5k 大小,要是 jpg 和 png 都不敢想。至少大 10 倍、20 倍。

技術實現#

這裡我們準備一個新倉庫用來解釋這一切,後續也可以作為分享內容。

代碼在這裡 https://github.com/Otto-J/serverless-dynamic-banner

我先去寫代碼了,寫完代碼回來補充。

經過一些時間後,終於寫完了,serverless 服務也上線可用了。大量吐槽來了,氣人😡。

踩坑經驗#

之前講到本次技術選擇是:nitro + 騰訊雲 serverless,目標是上線 serverless 服務,最終我們手裡有一個網址,一打開是一張圖片。

Nitro 文檔稀爛#

我選擇 nitro 有兩個原因:

  • 不用我學習新語法,比如 next.js 的那些概念,輕鬆上手,比 express/koa 相比不是玩具,大量內置了 ts、路由、中間件、常用方法,更快更容易寫出真實業務代碼
  • Nitro 是 nuxt 的組成部分,學會了 nitro 一魚兩吃,寫後端服務、寫全棧服務都可以用得上,經驗直接復用

問題來了,如果我想獲取路由的信息,設置返回的信息等技術細節,我應該怎麼學會呢?問 GPT,GPT 不知道這麼新的東西。只能看文檔和教程。

Nitro 文檔就幾頁,沒中文,功能介紹跟 ppt 像的,也沒有 playground,看的眼睛都花了,也沒找到 api 索引。生氣,guide+api 索引 + playground + 項目實戰不應該標配嘛!

image.png

不過我是高手,好在我知道 Nitro ([[速通 - unjs 家族的 Web 框架 Nitro]]) 是 unjs/h3 ([[unjs_h3]]) 的封裝,h3 提供低級 api,nitro 提供高級封裝開箱即用。所以我應該去看 h3 的文檔,聰明如我。

github 搜索 unjs h3,打開 https://github.com/unjs/h3 結果...

image.png

算了,告訴你吧,api 索引在這裡,在 h3 長長文檔的最下方

image.png

如何設定返回值時,指定 content-type 為 svg?自己試。結論是使用 send 方法。

對了,如果你是 Nuxt 用戶,那麼你需要按照這個路線學習 Nuxt 文檔 - Nitro 文檔 - h3 文檔。

騰訊 Serverless 函數體驗贼差#

之前文檔也提到過 ([[速通 - 騰訊雲雲函數 scf]]),作為國內大廠,值得信賴,而且做了也不是一年兩年了。

結果體驗贼差,我以為的雲函數上線過程:

  • 授權 github 信息,讀取項目
  • 選擇項目源碼,自動或者手動選擇基本信息,幫我做好構建
  • 拿到交付產物,上傳到雲端
  • 給我一個地址,我一點擊就訪問通了
  • 告訴我你可以自定義網址,為了安全和推廣其他服務,建議你開通網關鑑權、監控、日誌之類的
  • 用吧,額度不夠可以買套餐

挺合理的,服務好用戶,也能帶一帶其他商業服務。

實際上不是,每一步都有大量的錯誤,大量的延伸技術概念,大量的問號幫助文檔。生怕我覺得前端上線個服務挺簡單,沒有技術難度。

無力吐槽,算了,不寫了,好在走過來了。

image.png

image.png

image.png

後面單獨開個文檔介紹吧。

DNSPOD 扭扭捏捏不融入騰訊雲#

DNSPOD 被騰訊雲收購了,現在屬於騰訊雲。即便我域名、備案、解析都在騰訊雲和 DNSPOD,我開通 DNS 解析,還是要重複開頁面、登錄、掃碼、編寫。

麻煩死了。

自己愚蠢文件放錯了位置#

這個看我源碼就知道,為了目錄好看,我包了一層,結果腦抽忘了,一些文件放外層了,怎麼都拿不到,嘿嘿,後面不同項目開不同的窗口,亂了就不好了。

satori 不支持 css 和圖片的特性#

因為 svg 技術限制,satori 不支持 z-index,不支持 transfrom,必須全程使用 flex 布局,字體必須指定,不支持 webp/avif 等圖片格式,不支持 hsl 顏色格式。

怎麼說呢,好在東西不複雜,樣式改改也就能用了。

我 banner 不是有一個小人頭像麼,svg 是要通過 base64 內嵌進去的,體積會變大。為了減少圖片體積,我嘗試轉 svg,轉 avif/webp 等格式,都不是很滿意。

最後用的 png 顏色減少,感覺 24 色就足夠了,輕鬆讓 png 縮小了五六倍,肉眼看不出差異,挺好的。

總結展望#

東西做完了,感受如何,昨天都氣瘋了,今天算寫完了。

下次還來,希望輕車熟路,能快一點。

做國內的雲服務雖然有價格戰,但還是有位置的,業務還是要專注、做好。服務使用流暢應該是標配的。

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。