====== 第九部分:高级特性 ======
===== 9.1 二进制数据存储 =====
===== 9.1.1 Blob 和 File =====
IndexedDB 可以直接存储 Blob 和 File 对象:
// 存储图片
async function storeImage(file) {
const transaction = db.transaction(['images'], 'readwrite');
const store = transaction.objectStore('images');
const imageRecord = {
id: generateId(),
name: file.name,
type: file.type,
size: file.size,
data: file, // 直接存储 File 对象
uploadedAt: new Date().toISOString()
};
return new Promise((resolve, reject) => {
const request = store.add(imageRecord);
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
// 读取图片并显示
async function loadImage(id) {
const transaction = db.transaction(['images'], 'readonly');
const store = transaction.objectStore('images');
const record = await new Promise((resolve, reject) => {
const request = store.get(id);
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
if (record) {
const url = URL.createObjectURL(record.data);
const img = document.createElement('img');
img.src = url;
document.body.appendChild(img);
}
}
===== 9.1.2 ArrayBuffer 和 TypedArray =====
// 存储二进制数据
async function storeBinaryData(id, arrayBuffer) {
const transaction = db.transaction(['binaryData'], 'readwrite');
const store = transaction.objectStore('binaryData');
const record = {
id,
data: arrayBuffer,
byteLength: arrayBuffer.byteLength,
storedAt: new Date().toISOString()
};
await store.put(record);
}
// 存储 TypedArray
async functionStoreTypedArray(id, floatArray) {
const transaction = db.transaction(['numbers'], 'readwrite');
const store = transaction.objectStore('numbers');
await store.put({
id,
data: floatArray, // Float32Array 等
type: floatArray.constructor.name
});
}
===== 9.2 在 Web Worker 中使用 =====
===== 9.2.1 Worker 中的数据库操作 =====
// worker.js
let db = null;
self.onmessage = function(event) {
const { action, data } = event.data;
switch (action) {
case 'open':
openDatabase(data.dbName, data.version);
break;
case 'add':
addData(data.storeName, data.record);
break;
case 'get':
getData(data.storeName, data.key);
break;
case 'query':
queryData(data.storeName, data.index, data.range);
break;
}
};
function openDatabase(dbName, version) {
const request = indexedDB.open(dbName, version);
request.onupgradeneeded = function(event) {
const db = event.target.result;
if (!db.objectStoreNames.contains('data')) {
db.createObjectStore('data', { keyPath: 'id' });
}
};
request.onsuccess = function(event) {
db = event.target.result;
self.postMessage({ type: 'ready' });
};
request.onerror = function(event) {
self.postMessage({ type: 'error', error: event.target.error.message });
};
}
function addData(storeName, record) {
const transaction = db.transaction([storeName], 'readwrite');
const store = transaction.objectStore(storeName);
const request = store.add(record);
request.onsuccess = () => {
self.postMessage({ type: 'added', key: request.result });
};
}
function getData(storeName, key) {
const transaction = db.transaction([storeName], 'readonly');
const store = transaction.objectStore(storeName);
const request = store.get(key);
request.onsuccess = () => {
self.postMessage({ type: 'result', data: request.result });
};
}
// main.js
const worker = new Worker('worker.js');
worker.postMessage({
action: 'open',
data: { dbName: 'WorkerDB', version: 1 }
});
worker.onmessage = function(event) {
if (event.data.type === 'ready') {
console.log('Worker 数据库就绪');
// 发送数据
worker.postMessage({
action: 'add',
data: {
storeName: 'data',
record: { id: 1, value: '测试数据' }
}
});
}
if (event.data.type === 'added') {
console.log('数据已添加:', event.data.key);
}
};
===== 9.3 大数据集处理 =====
===== 9.3.1 流式处理 =====
// 处理大量数据,避免内存溢出
async function* streamRecords(storeName, batchSize = 100) {
const transaction = db.transaction([storeName], 'readonly');
const store = transaction.objectStore(storeName);
let batch = [];
await new Promise((resolve, reject) => {
const request = store.openCursor();
request.onsuccess = (event) => {
const cursor = event.target.result;
if (cursor) {
batch.push(cursor.value);
if (batch.length >= batchSize) {
resolve();
} else {
cursor.continue();
}
} else {
resolve();
}
};
request.onerror = () => reject(request.error);
});
if (batch.length > 0) {
yield batch;
}
}
// 使用
for await (const batch of streamRecords('logs', 500)) {
console.log('处理批次:', batch.length);
await processBatch(batch);
}
===== 9.3.2 数据导入导出 =====
// 导出为 JSON
async function exportToJSON(storeName) {
const transaction = db.transaction([storeName], 'readonly');
const store = transaction.objectStore(storeName);
const records = await store.getAll();
// 处理不可序列化的数据
const serialized = records.map(record => {
const copy = { ...record };
// 将 Blob 转为 base64 或标记
if (copy.data instanceof Blob) {
copy._hasBlob = true;
copy._blobType = copy.data.type;
delete copy.data; // 不导出二进制数据
}
return copy;
});
return JSON.stringify(serialized, null, 2);
}
// 从 JSON 导入
async function importFromJSON(storeName, jsonString) {
const records = JSON.parse(jsonString);
const transaction = db.transaction([storeName], 'readwrite');
const store = transaction.objectStore(storeName);
for (const record of records) {
await store.put(record);
}
}
===== 9.4 复合索引高级用法 =====
===== 9.4.1 多字段查询 =====
// 创建复合索引
const store = db.createObjectStore('products', { keyPath: 'sku' });
store.createIndex('categoryPriceIndex', ['category', 'price']);
store.createIndex('brandRatingIndex', ['brand', 'rating'], { unique: false });
// 查询特定分类中价格范围内的产品
function findProducts(category, minPrice, maxPrice) {
const transaction = db.transaction(['products'], 'readonly');
const store = transaction.objectStore('products');
const index = store.index('categoryPriceIndex');
// 复合索引的范围查询
const range = IDBKeyRange.bound(
[category, minPrice],
[category, maxPrice]
);
return index.getAll(range);
}
// 查询特定品牌的所有产品(忽略第二个字段)
function findByBrand(brand) {
const transaction = db.transaction(['products'], 'readonly');
const store = transaction.objectStore('products');
const index = store.index('brandRatingIndex');
// 只指定第一个字段,第二个字段用范围
const range = IDBKeyRange.bound(
[brand, -Infinity],
[brand, Infinity]
);
return index.getAll(range);
}
===== 9.5 存储持久化 =====
===== 9.5.1 请求持久存储 =====
async function requestPersistentStorage() {
if (navigator.storage && navigator.storage.persist) {
const isPersistent = await navigator.storage.persist();
if (isPersistent) {
console.log('已获取持久存储权限');
} else {
console.log('持久存储请求被拒绝');
}
return isPersistent;
}
return false;
}
// 检查存储持久化状态
async function checkPersistence() {
if (navigator.storage && navigator.storage.persisted) {
const persisted = await navigator.storage.persisted();
console.log('存储是否持久化:', persisted);
return persisted;
}
return false;
}
===== 9.6 本章小结 =====
本章介绍了 IndexedDB 的高级特性:
* 支持 Blob、File、ArrayBuffer 等二进制数据
* 可以在 Web Worker 中异步处理数据
* 流式处理大数据集避免内存问题
* 复合索引支持多字段查询
* 可请求持久存储防止数据被清理
继续阅读 [[part10-performance|第十部分:性能优化]]。