圖片壓縮

圖片壓縮

目標:將 png 圖片轉換成 webp 格式,以提升網頁瀏覽速度並且減少伺服器 loading

注:這是當初工作的個人筆記,已將敏感部分替換掉

參考自以下網站:

  1. 使用 AWS Lambda 提高网站图片加载速度 1X 倍

  2. 圖片縮放指南

初步試作

步驟:

  1. 建立一個放圖片的 S3 之後,到 CloudFront 新增一個 distribution 指定剛剛建好的 S3

  2. 建立新的 OAI、更新 S3 的存取政策

  3. 更新 Action 為 s3:ListBucket,更新 Resource 為 S3 的 ARN

  4. SAR(圖片縮放的 Repo)建立 Lambda Edge
    建立後指定剛剛新增的 CloudFront,CloudFront Event 要選擇 Origin Response

問題:

  1. 如果有更新程式碼,就要佈署新版本到 Lambda@edge

  2. 單純照著圖片縮放指南的步驟,可以成功地重新裁切圖片至指定大小,但是沒辦法將圖片轉換為其他格式

  3. 有修改了 app.js 的 Content Type,但是無法解析該請求

環境問題

  1. 發現 SAR 只有 resize ,沒有轉換圖片的功能,但也有可能是 Origin Request 沒辦法修改,所以拿不到 Webp 的圖片

  2. 安裝 CDK 發現 Node.js 過舊,所以要更新版本(但是不知道其他專案有沒有限定版本的 Node)

sudo rm -rf /usr/local/bin/npm /usr/local/share/man/man1/node* ~/.npm
sudo rm -rf /usr/local/lib/node*
sudo rm -rf /usr/local/bin/node*
sudo rm -rf /usr/local/include/node*

sudo apt-get purge nodejs npm
sudo apt autoremove

# 安裝 node
sudo npm cache clean -f
sudo npm install -g n
sudo n stable

重新評估執行步驟

如果只要照著步驟做,並且將 Lambda 的程式碼更新的話,還要做哪些事情?

  1. 因為目前網站的 S3 包括人物、簡報、白皮書與廣告的原始圖片,所以之後要建立一個新的 S3,專門放 webp 格式的圖片,並且將舊的圖片搬過去

  2. S3 有 ACL 的設定,所以「公有存取權」有關 ACL 的設定要取消勾選

  3. S3「物件擁有權」也要指定為「物件選擇器」

前輩提供的建議與步驟

前輩昨天找到另一篇文章,可以直接將圖片轉成 webp。

(但是我認為這樣很麻煩,因為之後可能還要寫排程,定期更新圖片,也不能及時處理需求,長遠來看並不是好方法)

圖片轉檔功能:

  • AWS 服務:

    • 新增:S3 + CloudFront + Lambda

    • 舊的 S3 搬到新的 s3

    • 新的 cloudfront 連新的 s3

    • Lambda 的部署、程式碼上傳

    • IAM Role 的設定

    • 測試機、正式機使用的 cloudfront 跟 s3 是分開的,所以請 DEV 建置的話,要說明清楚

  • 後台:

    • 可以用指令 批次修改 檔案紀錄資料表 圖片的 disk、url 值

    • 新增圖片用的 disk(filesystems.php)

    • 修改圖片上傳的 disk、.env(AWS_BUCKET、AWS_URL)

    • 檢查圖片以外的檔案上傳功能是否正常運作

    • 檢查圖片上傳是否正常丟到新的 s3 位置

  • API:

    • 修改圖片副檔名

    • 建議此功能更新的順序:

      1. 先處理 aws 的服務

      2. 再更新後台程式:

        • 設定 .env

        • 清 config

        • 批次修改 檔案紀錄資料表 中,disk、url 欄位

      • 最後更新 API 程式

瓶頸

我不知道要怎麼更改跟圖片相關的設定,以下是我目前知道的事情:

  1. 要建立一個新的 S3,並且使用 Lambda@edge 跟 CloudFront 處理圖片;

以下是我覺得也許做得到的事情:

  1. 既然舊的 S3 已經有圖片了,那麼能不能在取得圖片時,加上一層 Lambda 來轉換成 webp?
    因為它只會轉換指定 URI 的圖片,其他前綴不同的圖片則不會被轉換

  2. 以正式機為例,目標是解析特定前綴為 /site-name/image/ 的圖片,新的圖片則會跟舊圖片放在同一個資料夾底下

  3. 建立新的 CloudFront ,選擇指定的 S3 後,要另外建立一個新的 OAI

  4. 透過 SAR 建立 Lambda

  5. 打開函數的壓縮檔,更改 app.js 中的 S3 名稱,再上傳修改後的壓縮檔

  6. 佈署至 Lambda@edge,CloudFront Event 選擇 “Origin Request”

與 DEV 說明

因為圖片是透過 CloudFront 和 Lambda 處理,所以在第一次接收到圖片的請求時,會在 S3 生成並回傳新的 .webp 圖片

佈署之後,如果前端 call API 後,沒有在圖片的 URL 後面加上 .webp 的副檔名,就不會取得 webp 圖片

所以在前端沒有更新,但已經佈署好 Lambda 的情況下,我們可以在既有的圖片 URL 後面加上 .webp 測試

因此我覺得不會影響到目前的平台

由於這篇文章的目標是新建一個服務,所以我想補充在已有 S3 和 CloudFront 的情況下,步驟之間的差異

Lambda:

  1. 使用 SAR 建立 Lambda,參數隨意,因為會被覆蓋

  2. 透過 CloudFormation 佈署完成後,點選 Resource 中的 ResizeLambdaEdgeFunction

  3. 打開函數的壓縮檔,更改 app.js 中的 S3 名稱,再上傳修改後的壓縮檔,以更新程式碼

  4. 更新程式碼後,點選 操作->佈署至 Lambda@edge

  5. 建立新的 CloudFront Trigger:Distribution 選擇與 S3 串接的 CloudFront;
    Event 選擇 “Origin Response”,勾選佈署到 Lambda@edge 後按部署鍵

CloudFront:

  1. 在 S3 串接的 CloudFront 建立行為,有以下三個參數需要設定

  2. 路徑模式:填上 /site-name/image/*
    來源:選擇圖片的 S3
    功能關聯:來源回應 → 函數類型,選擇 Lambda@edge 並且填上對應的 ARN

DEV 說明現況

DEV 說因為 cloudfront 跟 Lambda@edge 的帳號不同,所以沒辦法串接

如果在 cloudfront 的帳號佈署 lambda,且 cloudfront 已和另一個帳號的 S3 串接的情況下,應該能夠正常運作才對

總結和懶人包

Lambda:

  1. 使用 SAR 建立 Lambda,參數隨意,因為會被覆蓋

  2. 透過 CloudFormation 佈署完成後,點選 Resource 中的 ResizeLambdaEdgeFunction

  3. 打開函數的壓縮檔,更改 app.js 中的 S3 名稱,再上傳修改後的壓縮檔,以更新程式碼

  4. 更新程式碼後,點選 操作->佈署至 Lambda@edge

  5. 建立新的 CloudFront Trigger:Distribution 選擇與 S3 串接的 CloudFront;
    Event 選擇 “Origin Response”,勾選佈署到 Lambda@edge 後按部署鍵

CloudFront:

  1. 在 S3 串接的 CloudFront 建立行為,有以下三個參數需要設定

  2. 路徑模式:填上 /site-name/image/*
    來源:選擇圖片的 S3
    功能關聯:來源回應 → 函數類型,選擇 Lambda@edge 並且填上對應的 ARN

S3:

  1. 由於公司的 S3 和 Cloudfront 分屬不同帳號,因此佈署 Lambda 之後,要再到 S3 的許可政策設定權限
{
  "Version": "2012-10-17",
  "Statement":
    [
      {
        "Sid": "AllowLambdaAccess",
        "Effect": "Allow",
        "Principal": { "AWS": "arn:aws:iam::{$ACCOUNT_ID}:role/{$ROLE_NAME}" },
        "Action": ["s3:GetObject", "s3:PutObject"],
        "Resource": "arn:aws:s3:::{$BUCKET_NAME}/*",
      },
    ],
}

後記

  1. 這個做法跟這篇部落格文章不同,它分成了 Viewer Request 和 Origin Response 兩個函數,但我們全放在同一個函數,所以可能會比較慢一點

  2. 目前平台需要轉成 webp 的圖片總數量不超過萬張,若未來需要將全部圖片轉換成 webp ,粗略估計也不會超過十萬張;而上述的部落格文章提到的是每百萬張的成本為 90 元人民幣,由此可見此服務的效益是很不錯的

  3. 面試被問倒後才發現,當初其實沒有考慮儲存空間,只有想著要把自動轉檔的功能做出來 😓,沒有和前輩們討論後續維護。(但印象中是說 S3 有生命週期,讓 DEV 之後去煩惱就好 🤔?)

Weird Parts 型別轉換與創造物件

圖 1

經過將近三個月的沉澱,以及觀察已經上岸的同學們,深刻體悟到一個大盲點:學習新框架或是工具,甚至是做 Side Project 都很好,但是如果最基礎的 JavaScript 都不甚理解的話,日後無論學習或是求職必定是事倍功半。

於是在上禮拜,終於購買了鼎鼎大名的 JavaScript: Understanding the Weird Parts並且深深地感到後悔。

後悔為什麼不早點買。

所以預計接下來的筆記內容會以課程內容為主。


強制轉型

  1. console.log(3 < 2 < 1)console.log(1 < 2 < 3) 會印出什麼?

    3 < 2 會被轉成 false,而當 false < 1
    會將 false 轉回 0,因此第一個答案會是 true

    同樣地, 1 < 2 會被轉成 true
    true < 3 會讓 true 又轉成 1,第二個答案也會是 true

  2. 如果有兩個 library 或 framework 設定的全域物件名稱相同

    會以最後呼叫的檔案中的變數為主。
    globalObject1.png

    所以看到像上圖這樣呼叫 window 物件,是為了避免被後面的變數取代,利用強制轉型,使 libraryName 變成 “Lib1” ,再利用邏輯運算子的特性讓 window.libraryName = “Lib1”


創造物件

  1. Object() constructor 物件建構子

    1. 無實例,以下兩個範例有相同的結果

      let person = new Object // 範例1
      
      let person = {} // 範例2
      
    2. Object.create() 已有實例,則可套用該物件的格式建立另一個物件

  2. Object Literals Syntax 物件實字語法

    1. Property accessors 告訴我們點記法和中括號記法兩者的結果是相同的,但在創立物件和屬性的時候,如下使用語法都會出現錯誤 Uncaught TypeError: Cannot set property 'firstName' of undefined。

      const profile = {}
      profile.name.firstName = 'Casper'
      profile[name][firstName] = 'Casper'
      

      因為運算子的相依性,會被認定為 undefined
      所以需要先建立一個 profile.name = {}

      const profile = {}
      profile.name = {} // 加上這行
      profile.name.firstName = 'Casper'
      
    2. 利用物件實字語法就不會出現以上的情況,也更加簡潔與方便。
      在 JS 中沒有「命名空間」,但是可以像下面的例子一樣,建立一個類似 C++ 中類似命名空間的容器,避免命名衝突。

      const profile = {
        name: {
            firstName: 'Casper'
          }
      }
      
    3. JSON 是受到物件實字語法的便利性所建構的一種格式,主要簡化了許多不必要的符號,得以讓檔案縮小並提升傳輸效率。
      也因為 JSON 比起物件實字語法要來得嚴格,可以快速分辨兩者之間的差異。
      最主要的差異是在最外層需要用引號包裝,鍵也必須是以雙引號""包裝的字串。

      const objectLiteral = {
          firstname: 'Casper',
          isAProgrammer: true
      }
      
      console.log(JSON.stringify(objectLiteral))
      
      const jsonValue = JSON.parse('{ "firstname": "Casper", "isAProgrammer": true }')
      
      console.log(jsonValue)
      

    JsonExample

參考資料

  1. Why the object[bar] has the same output as object[foo]?
  2. JSON.parse() - JavaScript | MDN (mozilla.org)
架設 HEXO 部落格的理由

Photo by Michael Sum on Unsplash

以前最先碰到的筆記工具是 HackMD,不過因為排版上必須使用 Markdown 語法,覺得不太方便,所以發現 Notion 後,就再也沒使用過 HackMD。

如果想要和其他人分享心得的話,發在 Medium 上,除了感覺比較正式之外,用 Notion 打好草稿後還是得處理麻煩的排版問題。
更重要的是,程式碼區塊真的很不易讀,因此開始尋找能夠客製化以及方便管理的工具。

曾經考慮過 Github 的個人頁面以及 WordPress,最後爬過幾位前輩的文章後,才決定使用 Hexo 和 Netlify 來建立個人部落格。

以後會把技術相關的文章放在 Hexo上;較為個人的、或是工作求職上的心得仍會發在 Medium 上。


踩雷經驗分享

有幾點想要另外分享給大家,以免踩到同樣的雷

以下是用 Windows 10 所建構的步驟,使用前也得先安裝最新的 nvm,請多加留意

注意 NodeJS 的版本

  1. 以現在的 Hexo 6.0 為例,LTS(Long Term Support) 為 12.13.0
  2. 以我的例子來說,之前學習 JS 的時候是 10.15.0 ,所以需要 Hexo 所推薦的 NodeJS 版本
    $ nvm install 12.13.0
    
  3. 安裝完畢後,得再輸入$ nvm use 12.13.0 才能夠使用剛安裝好的版本
  4. 最後用 $ node -v 確認目前版本是否正確

字型的設定

uglify1

看起來非常亂,是因為 AOMORI 主題本身有使用 Gulp 作為打包工具,將 CSS 和 JS 的部分 minify 與 uglify

其實花蠻多時間在調整 CSS ,也有一些可以反向轉譯的網站可以使用,不過一直切畫面也是蠻煩躁的,最後還是用搜尋功能來直接修改。


入坑學習心得

感謝 Shelly 的文章,可以讓我無痛入坑,並且大力推薦 Kyo 的心得給同樣想使用 Notion 打草稿的朋友~

目前還是看得出來有很多需要修改的地方。Webpack 和 Gulp 的使用方法與心得也值得多加探討。
不過跟 Medium 相比,如果要發 LeetCode 的題目解析或是學習心得,Hexo 會更加易讀。

參考文章

  1. Hexo 無痛入坑囉 by Sherry
  2. Notion + VS Code:我的 Hexo Markdown 寫作工作流 by Kyo
  3. [week 13] 前端工具之三 - gulp、webpack by Heidi-Liu