從 AWS SES 轉寄信件無法正確顯示內嵌圖片談起:MIME 內嵌圖片原理與實踐

我遇到了什麼問題 最近我用 SES, S3, Lambda 開發了轉寄信件的功能,簡單介紹一下這功能: 假設我就職於公司的開發部門,我們對外有一個信箱是 dev@example.com 當客戶遇到軟體開發上的問題時寄信到 dev@example.com,開發部門的所有同仁就會在自己的信箱收到此客戶寄來的電子郵件,重點是開發部門的同仁們都可以用自己的帳號回覆該客戶 而開發好這功能後,我遇到一個問題 — 轉寄出去的信件沒辦法正確顯示內嵌圖片 關於轉寄信件的程式碼我放在 GitHub 上囉 (連結),之後有時間會寫一篇 Forward Email 的完整教學,或是也可以關注我的 Notion,我每天都會寫筆記記錄自己的學習過程 讓我們直接看例子 首先,我登入私人帳號模擬我是客戶,寄信給 test@aws-educate.tw,下圖是當時寄出去的內容: 寄給 test@aws-educate.tw 後,SES 會收到信,然後根據我配置的 Receipt rule,原始郵件檔案會被儲存至我指定的 S3 Bucket 延續 用 SES, S3, Lambda 實現轉寄信件功能 ,當我嘗試在轉寄出去的信件中內嵌圖片,最後收到轉寄來的信,都會出現下圖的現象:圖片沒辦法內嵌在裡面,倒是跑去附件了 寫文章時,已經把原本轉寄過來的信件刪了,所以下圖信件主旨跟前一張圖片不一樣不要介意,下圖信件已經詮釋我想表達的現象了 XD 我起初嘗試的解法 後來下面這篇 Stack Overflow,裡面有人提到 Gmail 還不支援顯示 base64 編碼的圖片 (有待確認,希望有人可以告訴我正確答案) As of January 2020, Gmail still does not support base64 encoded images. Source: Add embedded image in emails in AWS SES service...

August 6, 2024 · 2 min

在 Next.js 音樂應用中使用動態 Preload 技術提升前端效能降低延遲

程式碼在這邊的 feature/preload 分支: https://github.com/sh1un/Nextjs-Musive-app 此專案是一個 Next.js 專案,原作者為 Ansh Rathod,我已經過作者本人授權使用,因此 Fork 過來稍微改了一下。 我本身不是一名專精於前端技術的開發者,所以我所修改的 Code 都是由 ChatGPT-4 產生的。我今天這樣做的主要目的是想實驗 preload 是否能做到效能優化,以此來做一個簡單的 PoC。 補充一下,這篇文章的內容本來是要放在 「2024 AWS Educate 陪跑計畫的獎勵課程 - 雲端串流挑戰:復刻 Spotify 的技術旅程」中教學,此工作坊旨在教學如何利用 CloudFront 優化速度與降低延遲,但礙於當天工作坊的內容太滿已經塞不下了,所以當天工作坊就沒有講到 Preload,如果看到這篇文章,想要學學 CloudFront 歡迎參觀我們當天的教材 (連結) 什麼是 Preload? Preload 是一種網頁性能優化技術,讓瀏覽器在頁面加載時預先加載指定的資源,這樣在使用這些資源時可以更快地顯示或播放。這可以減少等待時間,提升用戶體驗。 你可以想成:原本你是要“按下去播放按鈕”才會開始下載音樂,現在我們提前幫你下載,你之後“按下去播放按鈕”就會立即播放。 舉例 如果你在網頁上有一段影片或音樂,你可以用 preload 告訴瀏覽器提前下載這些文件,這樣當用戶點擊播放按鈕時,影片或音樂就會立即播放,而不是先等待下載。 基本用法: 在 HTML 中,你可以在 <link> 標籤中使用 rel="preload" 來預加載資源。例如: <link rel="preload" href="path/to/your/file.mp3" as="audio"> 這樣,瀏覽器會在頁面加載時預先下載 file.mp3,提高用戶播放音樂的速度。 動態 Preload 的實現思路 每個用戶的歌單都不一樣,所以我們當然不希望把 preload 的 href 寫死成一個 URL。 解決方案 我們可以通過以下幾個步驟來實現動態的 preload: API 獲取歌單: 伺服器端提供一個 API,根據用戶的請求返回該用戶的歌單。這個 API 返回一個 JSON 結構,其中包含了音樂檔案的 URL 列表。 JavaScript 動態生成 Preload 標籤: 使用 JavaScript 在頁面加載後動態地從 API 獲取歌單,並為每個音樂文件創建 preload 標籤,將其添加到頁面的 <head> 中。 具體實現 注意⚠️ 我的寫法並非添加 preload 到頁面的 <head>,而是直接用 React 和 JS 本身提供的內建物件來達到同樣效果,詳見本段說明。...

July 21, 2024 · 4 min

2024 伊雲谷實習計畫 - 雲端工程師實習心得

大家好,我是 Shiun,今天想和大家分享我在 eCloudValley MSP 部門擔任雲端工程師實習生的經歷。這段實習讓我學到了很多實用的雲端技術,同時也在工作中遇到了許多有趣的挑戰。 實習工作內容 在 eCloudValley 的實習期間,我主要負責以下幾項工作: 學習使用公有雲,包括 AWS 和 Azure。 開發公司內部的 GenAI 應用。 與同期且同部門實習生共同開發專案。 定期與 Mentor 開會。 參與最終展演。 撰寫系統文件。 實習期間大事紀 專案大改方向 在 eCloudValley 實習期間,我們的專案經歷了一次重大轉變。起初,我們的任務是開發一個 AI Bot 開單助理,目的是幫助客戶在雲端遇到問題時能夠迅速獲得支援。這個專案的初步構想是: 客戶向 AI 助理求助,並向 AI 助理提供必要資訊,例如: EC2 SG Inbound Rule, 健康檢查狀態… 等等 如果客戶提供的資訊不夠明確,AI 助理要明確地告知客戶還需提供哪些資訊。 AI 助理彙總客戶問題,轉化為技術人員易於理解的語言。 將資訊轉化後,開 ticket 到開單系統。 然而,在實作過程中,我們發現這個 AI 助理的效果不如預期,且後續優化存在技術瓶頸。客戶問題過於多樣化、且問題幾乎都很具情境,不是那種有標準解答或是標準 SOP 就能解決的問題,所以 AI 助理難以處理,導致準確率低,最終還是需要人工介入,反而增加了工作量。 因此,我們在 4 月底決定將專案轉變為一個「會議錄影機器人」。這個機器人會被邀請到 LINE 群組中,默默記錄每一條訊息以及圖片 (圖片透過 Claude 3 Sonnet 取得 Caption)。TAM 只需在群組中輸入特殊指令或貼圖即可控制錄影,最終在前端頁面查看記錄並由 LLM 總結,創建 Ticket 到 MSP 部門的 Ticket System。...

July 20, 2024 · 1 min

如何使用 Lambda 處理 AWS Cognito User 首次登入被系統要求強制變更密碼

情境描述 上圖顯示的是我剛在 Cognito User Pool 中新增了一個 user,可以注意到箭頭處顯示 Force change password。 當這個 user 嘗試首次登入時,他們會遇到這樣的情況: { "message": "New password required", "challengeName": "NEW_PASSWORD_REQUIRED", "session": "AYABeAsYJsR3yEr0iJssKPPUPEgAHQABAAdTZXJ2aWNlABBDb2duaXRvVXNlclBvb2xzAAEAB2F3cy1rbXMAS2Fybjphd3M6a21zOnVzLXdlc3QtMjowMTU3MzY3MjcxOTg6a2V5LzI5OTFhNGE5LTM5YTAtNDQ0Mi04MWU4LWRkYjY4NTllMTg2MQC4AQIBAHhPj7k9zU4nGXUQUvM0Ccwk42DS-fm3vKmH75ktTrktNQG1gnjl6HkUVUYN1J_HPow6AAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMulTha32s34j2CmQWAgEQgDtog8CDFh2e-4YyjM2kB_MXheMgmrdY_IF3aN9TImZXddMBj7djEAPPduLZnG3ddBLYQa8x3T3WPKUkvwIAAAAADAAAEAAAAAAAAAAAAAAAAADCbdmpLPo0E4QkWLlyH8ov_____wAAAAEAAAAAAAAAAAAAAAEAAAC1oMtgshmuUU4fk36WHKBzPgJEoE1MmL0PFyhR9lRcimImOIObhhxvC1fwiYylgbYx0Gu0i1cp5Le8AvrAnUGEJjZp54TMPP4N-JCT3qSrHeq_Kat_2CuECVSQqkc1qH4z9FVOTvAnos4FrDSn2W6KvFfLo8YQh2LJxM1h3GdIeyYqj7Ipfk6PZKGYmV5P741rRMNcuYBtvE8Hq9gVqMbEPG-c5MppY_q9JoG9TyQRN7rVGlZf62_WtTqST2F3-DZPoXTMTyY", "challengeParameters": { "USER_ID_FOR_SRP": "shiun", "requiredAttributes": "[]", "userAttributes": "{\"email\":\"xxxxx@gmail.com\"}" } } Cognito 回傳了一個 NEW_PASSWORD_REQUIRED 的 challenge,同時給了我們一個 session token。這就是我們需要處理的 case。 解決方案 要解決這個問題,我們需要提供一個 change-password 的 API endpoint,讓 user 可以順利更改密碼。整個 flow 大致如下: User 首次登入 Cognito 回傳 NEW_PASSWORD_REQUIRED challenge 和 session token 前端帶著 session token 呼叫我們的 change-password API 密碼更改成功,user 順利登入系統 以下是一個處理這種情況的 Lambda function 範例:...

July 19, 2024 · 2 min

深度解析 AWS API Gateway 的 Lambda Proxy Integration 功能

在 AWS API Gateway 創建 method 時,我們可以指定他要把請求傳到哪個 AWS 服務或是其他 HTTP Endpoint…等等,而如果你選擇 Lambda,你會看到有一個選項是 Lambda Proxy Integration,那我把它勾選起來會怎樣呢? TL;DR 如果有打開 Lambda Proxy Integration,接著我們到那個 Resource 的 Method 頁面看一下,會看到下圖的提示 Proxy integration For proxy integrations, API Gateway automatically passes the backend output to the caller as the complete payload. 但也許沒有很好理解,所以我們後面會實際打 Code 來示範,但我這邊先稍微解釋一下打開這個功能的影響: 完整的 HTTP 請求資訊:啟用 Lambda Proxy Integration 後,API Gateway 會將整個 HTTP 請求打包成單一的 event object,並傳遞給 Lambda Function 簡化的處理流程:Lambda Function 接收到的是一個包含了所有請求資訊的 event object,因此不需要再通過模板或轉換就可以直接處理請求。這樣簡化了函數的輸入處理,因為 Lambda Function 已經包含了所有必要的請求資訊 控制 HTTP Response:Lambda Function 需要返回一個特定格式的物件,這個物件包括 Status Code、Response Headers 和 Response Body。由於 Response 是直接由 Lambda Function 生成的,所以開發者可以完全控制 Response 的格式和內容 效能和成本效益:由於請求處理的過程更加直接,減少了中間的處理和轉換步驟,可以提高處理效率,可能也會在某種程度上降低成本 總之,選擇 Lambda Proxy Integration 可以使得與 AWS Lambda 的整合更加緊密、直接和高效。這適合需要精細控制 HTTP 響應的場景,或者希望簡化 API 與 Lambda 之間交互的架構設計...

June 14, 2024 · 4 min