回答

收藏

前端用IDB缓存大文件, 减少服务端流量压力

前端知识 前端知识 563 人阅读 | 0 人回复 | 2024-07-18

本帖最后由 wangyang 于 2024-7-18 15:50 编辑

场景分析 为什么要把大文件存在前端?在某些应用场景下,对于需要频繁加载文件的web应用。 如在线编辑器,文件预览, 文件解析。 这样做。能够给服务器剩下很大一笔带宽。这么做是很有必要的。
使用需求场景代码如下: 前端根据记录的id,获取加载详情需要的本地文件。如果没有我封装的DB模块就会报错,就会走到下面的加载文件的后端接口,并把文件给保存到indexDB里,这样下次就不用走后端了。

  1. // 从本地读取文件
  2. try {
  3.   const dbReadFile = await Db.getValue(`pdfFile:${item.requestId}`)
  4.   if (dbReadFile) {
  5.     setCurPdf(URL.createObjectURL(dbReadFile));
  6.   }
  7. } catch (err) {
  8.   const file = await getFile(item.requestId);
  9.   setCurPdf(URL.createObjectURL(file));
  10.   await Db.setValue(`pdfFile:${item.requestId}`, file)
  11.   await saveFileTimeDb.setValue(item.requestId.toString(), new Date().getTime())
  12. }
复制代码
性能考量 前端存储可以减少服务器的负担,加快数据的读写速度,提升用户体验。
为什么选择IndexDB

  • LocalStorage:简单易用,但容量有限。
  • IndexedDB:功能强大,适合存储大量结构化数据。
  • Web SQL(已废弃)和FileSystem API(实验性)。

IndexedDB是什么? IndexedDB是一个运行在浏览器中的非关系型数据库,它允许网页应用存储大量数据。与传统的cookie和localStorage相比,IndexedDB提供了更复杂的数据存储能力。


主要特点

  • 大量数据存储:可以存储比localStorage更多的数据。
  • 异步操作:不阻塞主线程,提升网页性能。
  • 事务支持:保证数据操作的原子性。
  • 键值对存储:通过键来索引数据,提高数据检索效率。

使用场景


  • 存储大量数据,如用户数据、缓存的网页内容等。
  • 需要事务支持的场景,确保数据的一致性。

基本操作


  • 打开数据库:使用open方法打开或创建数据库。
  • 创建对象存储:在数据库中创建存储数据的对象存储。
  • 读取和写入数据:通过事务进行数据的读取和写入。
  • 索引:创建索引以优化查询性能。

为什么选择IDB? 在前端开发中,本地存储是一个常见需求。IDB这个包,提供了比原生IndexDB更简单, 更方便的方法,你不用再担心数据库版本同步问题。它会帮你解决


如何封装IDB? 我将IDB封装成了一个易于使用的CrxIndexDB类,它提供了getValuesetValuedeleteValue三个公共方法,让本地数据操作变得简单快捷。

封装代码代码如下:

  1. import { IDBPDatabase, openDB } from 'idb'

  2. /**
  3. *  封装indexDB方便background进行本地缓存
  4. *  暴露三个公共方法(异步调用):
  5. *  getValue
  6. *  setValue
  7. *  deleteValue
  8. *
  9. *  同时注册这三个方法的Message消息,便于contentScript调用
  10. */
  11. class CrxIndexDB {
  12.   private database: string
  13.   private tableName: string
  14.   private db: any

  15.   constructor(database: string, tableName: string) {
  16.     this.database = database
  17.     this.tableName = tableName
  18.     this.createObjectStore()
  19.   }
  20.   public async getAllData() {
  21.     // 打开数据库
  22.     const dbName = this.database;
  23.     const storeName = this.tableName;

  24.     const db = await openDB(dbName, 1, {
  25.       upgrade(db) {
  26.         // 这里假设对象存储已经存在,如果不存在可以在这里创建
  27.         if (!db.objectStoreNames.contains(storeName)) {
  28.           db.createObjectStore(storeName);
  29.         }
  30.       }
  31.     });

  32.     // 获取对象存储中的所有数据
  33.     const tx = db.transaction(storeName, 'readonly');
  34.     const store = tx.objectStore(storeName);
  35.     const allData = await store.getAll();

  36.     await tx.done;
  37.     return allData;
  38.   }
  39.   public async getValue(keyName: string): Promise<any> {
  40.     await this.dbReady()
  41.     const { tableName } = this
  42.     const tx = this.db.transaction(tableName, 'readonly')
  43.     const store = tx.objectStore(tableName)
  44.     const result = await store.get(keyName)
  45.     return result.value
  46.   }

  47.   public async setValue(keyName: string, value: any) {
  48.     await this.dbReady()
  49.     const { tableName } = this
  50.     const tx = this.db.transaction(tableName, 'readwrite')
  51.     const store = tx.objectStore(tableName)
  52.     const result = await store.put({
  53.       keyName,
  54.       value
  55.     })
  56.     return result
  57.   }

  58.   public async deleteValue(keyName: string) {
  59.     await this.dbReady()
  60.     const { tableName } = this
  61.     const tx = this.db.transaction(tableName, 'readwrite')
  62.     const store = tx.objectStore(tableName)
  63.     const result = await store.get(keyName)
  64.     if (!result) {
  65.       return result
  66.     }
  67.     await store.delete(keyName)
  68.     return keyName
  69.   }

  70.   private sleep = (num): Promise<boolean> => {
  71.     return new Promise((resolve) => {
  72.       setTimeout(() => {
  73.         resolve(true)
  74.       }, num * 1000)
  75.     })
  76.   }

  77.   private async dbReady() {
  78.     if (!this.db) {
  79.       await this.sleep(0.5)
  80.       return await this.dbReady()
  81.     }
  82.     return true
  83.   }

  84.   private async createObjectStore() {
  85.     const tableName = this.tableName
  86.     try {
  87.       this.db = await openDB(this.database, 1, {
  88.         upgrade(db: IDBPDatabase) {
  89.           if (db.objectStoreNames.contains(tableName)) {
  90.             return
  91.           }
  92.           db.createObjectStore(tableName, {
  93.             keyPath: 'keyName'
  94.           })
  95.         }
  96.       })
  97.     } catch (error) {
  98.       return false
  99.     }
  100.   }
  101. }
复制代码


分享到:
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

58 积分
16 主题
+ 关注