skrjs's Studio.

Threejs 模型本地存储

2021/08/22

Threejs 模型本地存储

还是之前那个模型查看的问题,有个模型 50M,每次打开都需要从服务器上请求模型,速度太慢被老板吐槽了,所以考虑将模型存在本地,具体的方案呢,我选定了使用 IndexedDB

我的思路是这样的:

  1. 首先在 indexedDB 查找有没有这个模型的数据存在
  2. 如果存在就将这个数据查询出来,加载本地的模型
  3. 如果不存在就从服务器请求模型数据,然后存在本地 indexedDB

但是有两个问题:

  1. 从服务器请求到的模型怎么转换成 blob 存进 indexedDB
  2. threejs 的 gltfLoader 需要提供的是一个确切的 url 地址

开始解决问题:

首先解决怎么把从服务器请求到的模型转换成 blob

1
2
3
4
5
6
7
axios({
url: url,
method: 'get',
responseType: 'blob',
}).then((res) => {
let blob = res.data
})

然后我们把这个数据存进 IndexedDB:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
let id = 'xxxxx' //这里随便弄的一个id

let indexedDB =
window.indexedDB ||
window.webkitIndexedDB ||
window.mozIndexedDB ||
window.msIndexedDB
if (!indexedDB) {
console.log('不支持indexedDB')
}

let request = indexedDB.open('userModels')
let db
request.onupgradeneeded = (event) => {
console.log('数据库更新')
db = event.target.result
// 判断是否有这个表
if (!db.objectStoreNames.contains('models')) {
// 创建表
let objectStore
//使用id作为主键
objectStore = db.createObjectStore('models', {
// 主键
keyPath: 'id',
})
}
}
request.onsuccess = (event) => {
//数据库打开成功
db = event.target.result
let transaction = db.transaction(['models'], 'readwrite')
let objStore = transaction.objectStore('models')
let req = objStore.get(id)
req.onsuccess = (e) => {
if (req.result) {
//拿到数据
console.log(req.result)
} else {
//未查询到数据
axios({
url: url,
method: 'get',
responseType: 'blob',
}).then((res) => {
//将模型输入存进数据库
let blob = res.data
db.transaction(['models'], 'readwrite')
.objectStore('models')
.add({ id: id, blob: blob })
})
}
}
}

第二个问题,blob 怎么转换成 url

URL.createObjectURL() 静态方法会创建一个 DOMString,其中包含一个表示参数中给出的对象的 URL。这个 URL 的生命周期和创建它的窗口中的 document 绑定。这个新的 URL 对象表示指定的 File 对象或 Blob 对象。

1
objectURL = URL.createObjectURL(object)

其中参数 object 是: 用于创建 URL 的 File 对象、Blob 对象或者 MediaSource 对象。

最终代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
function getRealUrl(id, url) {
return new Promise((resolve, reject) => {
let fileType = url.split('.')[url.split('.').length - 1]
//查找数据库里有没有
let indexedDB =
window.indexedDB ||
window.webkitIndexedDB ||
window.mozIndexedDB ||
window.msIndexedDB
if (!indexedDB) {
resolve(url)
return false
}
let request = indexedDB.open('userModels')
let db
request.onupgradeneeded = (event) => {
console.log('数据库更新')
db = event.target.result
// 判断是否有这个表
if (!db.objectStoreNames.contains('models')) {
// 创建表
let objectStore
objectStore = db.createObjectStore('models', {
// 主键
keyPath: 'id',
})
}
}
request.onsuccess = (event) => {
console.log('数据库打开成功')
db = event.target.result
let transaction = db.transaction(['models'], 'readwrite')
let objStore = transaction.objectStore('models')
let req = objStore.get(id)
req.onsuccess = (e) => {
if (req.result) {
console.log('已经查询到数据为:')
console.log(req.result)
let modelUrl = URL.createObjectURL(req.result.blob)
db.close()
resolve(modelUrl)
} else {
console.log('未查询到数据')
axios({
url: url,
method: 'get',
responseType: 'blob',
})
.then((res) => {
let blob = res.data
db.transaction(['models'], 'readwrite')
.objectStore('models')
.add({ id: id, blob: blob })
let modelUrl = URL.createObjectURL(blob)
db.close()
resolve(modelUrl)
})
.catch(() => {
db.close()
resolve(url)
})
}
}
}
})
}
CATALOG
  1. 1. Threejs 模型本地存储
    1. 1.0.0.1. 我的思路是这样的:
    2. 1.0.0.2. 但是有两个问题:
    3. 1.0.0.3. 开始解决问题:
  2. 1.0.1. 最终代码