系列:一張投影片背後的工程:從零打造網頁簡報編輯器
對應程式版本:Git tag
v0.4.0版本說明:本文聚焦匯入邊界的格式驗證與失敗時保留原專案,完整備份往返留到下一篇。
使用者選到的專案檔不能直接當成 PresentationDocument。TypeScript 型別只在開發期間生效,從磁碟讀進來的 JSON 仍是未知資料;即使 JSON.parse() 成功,欄位也可能缺少、重複或超出畫布。
匯入檔案上限為 32 MB。讀成文字後先捕捉 JSON 語法錯誤,再檢查備份版本、文件與資產陣列:
if (
!isRecord(value) ||
value.backupVersion !== 1 ||
!isPresentationDocument(value.document) ||
!Array.isArray(value.assets)
) {
throw new Error('The project file uses an invalid format.')
}
isPresentationDocument 會確認 schemaVersion、畫布尺寸、投影片順序與實際投影片完全對應,並要求至少一張未隱藏投影片。每張投影片的物件 ID 必須唯一,座標必須是有限的非負數、寬高必須為正數,而且整個 frame 要留在畫布內;文字、圖片與圖形也各自檢查允許的欄位和值。
解析器先從文件收集所有被引用的 assetId,再檢查每筆資產的 ID、PNG/JPEG MIME、尺寸、時間與 Data URL。重複 ID、錯誤的 Base64 前綴、缺少被引用圖片,或夾帶文件沒有使用的資產,都會讓整份專案被拒絕。
這不是只為了顯示友善錯誤。若文件先寫入、圖片後來才驗證失敗,使用者原本的專案就可能被半套資料取代。
介面先完整執行 readProjectBackup(),成功後才呼叫 saveProject(),在同一筆 IndexedDB transaction 寫入文件與圖片。等待寫入完成後,才用 replace() 更新畫面中的文件:
const project = await readProjectBackup(file)
await saveProject(project.document, project.assets)
replace(project.document)
任何一步失敗都會顯示「原本的簡報沒有變更」。測試也涵蓋無效 JSON、缺少被引用資產,以及含 Blob 的合法專案可以解析;匯入邊界因此有可重現的保護,而不是只靠 try/catch 猜測資料正確。
不過驗證只保證壞檔不會取代原簡報。合法的備份一匯入就會直接換掉目前的簡報,沒有額外確認;replace() 又會清空復原紀錄,選錯檔案就無法用 Ctrl+Z 找回,匯入前最好先下載目前的備份。解析器也只檢查 MIME 與 Base64 格式,不會解碼圖片;圖片資料損毀但格式正確的檔案仍會匯入,只是畫面上會出現破圖。這兩點到 v0.15.0 仍是如此。
目前 backupVersion: 1 遇到其他版本會直接拒絕。等格式真的需要演進時,再加入明確的遷移程序,比默默接受不完整資料安全。