前端如何緩存blob文件之圖片


曾經在網上看過:web開發其實就是json編解碼。仔細一想,這句話真是精辟。。。不過再往深處想,json編解碼只不過覆蓋了web開發的大部分(大部分時間都是在處理字符),其實還有一部分是處理二進制數據的(比如音視頻在線應用)。不過,現在前端也有出現了File,Blob,TypedArray等API,使得前端處理二進制數據特別方便。

這里說下我們的方案,鑒於安全方面的考慮,我們小組在處理圖片的時候不允許前端去直接拼接圖片路徑去訪問,應該通過后端接口去訪問(針對登錄用戶,header中需要帶token字段),導致我們前端訪問圖片的時候需要像普通接口(比如登錄,修改用戶手機號)一樣請求回來然后再處理。

我們使用Axios庫來進行ajax請求,前端需要在header中指明 responseType: 'blob' ,然后我們前端得到的就是blob類型的圖片文件。

接着我們需要在前端展示這個blob類型的圖片,這個嘛,so easy!URL.createObjectURL() api可以輕松搞定(使用對象URL顯示圖片)。

我封裝了一個函數,效果搞定了:

一會,測試過來跟我說,上邊兩個頁面切換的時候,加載時間過長(每次切換都請求圖片數據)。呀,這也簡單啊 ,我把 對象URL 加個緩存就就行了。

objectURL = URL.createObjectURL(object); 的返回值 是DOMString (一個UTF-16字符串),我們只要把 返回值存在localStorage ,sessionStorage,cookie,indexedDB,webSQL中任意一個即可,瀏覽器本地存儲字符串太在行了。沒有什么是緩存不能解決的,不過不能,就。。。

不一會,測試又跑過來了:退出公眾號,在進入到有圖片的頁面發現,圖片404了。。。哎呀,這是什么情況?

MDN解釋如下:

內存管理
在每次調用 createObjectURL() 方法時,都會創建一個新的 URL 對象,即使你已經用相同的對象作為參數創建過。當不再需要這些 URL 對象時,每個對象必須通過調用 URL.revokeObjectURL() 方法來釋放。
瀏覽器在 document 卸載的時候,會自動釋放它們,但是為了獲得最佳性能和內存使用狀況,你應該在安全的時機主動釋放掉它們。

當用戶在刷新頁面的時候,會先執行 document 卸載操作,這些 對象URL 也被垃圾回收掉了,當用戶再次進入的時候,訪問的還是緩存的資源,然而這個資源的實際內存已經不存在了,所以會出現圖片訪問404的問題。好比那句話,人活着錢沒了的感覺。。。

這個解決起來也簡單啊 ,我們在網頁卸載的時候主動清空這些緩存不就行了,最后用戶再次進入頁面的時候重新再去請求一次。想法有了,說干就干

瀏覽器有個 Window: beforeunload event api:

當瀏覽器窗口關閉或者刷新時,會觸發beforeunload事件。當前頁面不會直接關閉,可以點擊確定按鈕關閉或刷新,也可以取消關閉或刷新。

window.addEventListener('beforeunload', (event) => {
  // 執行我們清空圖片緩存的操作
  StorageUtil.clear()

  // Cancel the event as stated by the standard.
  event.preventDefault();
  // Chrome requires returnValue to be set.
  event.returnValue = '';
});

打完收工。。。

結果上真機上,發現微信瀏覽器根本不支持這個。。。后背一涼,不知道該咋辦了。算了,估計這條路走不通了,我就問了移動端的小伙伴咋解決這個問題的,他有可能會遇到跟web一樣的問題。移動端小伙伴:好像框架自動做了緩存(他們使用flutter),Image使用過一次會自動緩存,但是當app再次啟動的時候緩存會消失,需要重新請求一次。這不跟web上差不多嗎,只不過web上沒辦法判斷用戶何時進入頁面何時離開頁面罷了。

這問題了其實折騰了我一下午了,一陣尿意襲來,算求,上個廁所先。。。在噓噓的時候,我就在想:js如果能操作本地文件就好了,我可以把blob文件復制一份到客戶端某一目錄下,這樣實現緩存不就搞定了嗎!問題來了:前端如何把blob轉為文件存下來啊?

其實js是可以讀取客戶端文件的,比如我們在使用input上傳文件時,哎,有了。

FileReader api不就是讀取文件的嗎:

FileReader 對象允許Web應用程序異步讀取存儲在用戶計算機上的文件(或原始數據緩沖區)的內容,使用 File 或 Blob 對象指定要讀取的文件或數據。

我們可以把 blob 文件轉為 base64字符串啊:前端處理字符串太在行了。。。

var reader = new window.FileReader();
reader.readAsDataURL(blob); 
reader.onloadend = function() {
  console.log(reader.result);
}

最后,我將 blob 轉為 base64 緩存在localStorage。終於,功能完成了。

后續優化

我們知道轉base64會導致內存占用變大,本來3個字節結果變為了4個字節,我在開發時發現:我們的圖片轉為 base64 后大約在 30kb 左右(前提時對用戶上傳的圖片進行壓縮),有可能導致localStorage被占滿,我們需要提供刪除圖片緩存的機制:

  • 在用戶退出登錄的時候清空登錄數據和圖片緩存數據
  • 檢測本地緩存占用大小,提供用戶一個類似於app上清空緩存的操作,主動提示用戶去清空圖片緩存
  • 下下策,互相傷害,將別的域名下的localStorage刪掉,只保留本域名下的緩存(這招可太陰了)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM