第09章 存储和数据管理
12/3/25About 17 min
第9章:存储和数据管理
学习目标
- 掌握Chrome扩展的各种存储机制
- 学会设计高效的数据存储策略
- 实现数据同步和备份功能
- 优化存储性能和管理存储配额
9.1 存储机制概述
Chrome扩展提供了多种存储选项,每种都有其特定的用途和限制。
9.1.1 存储类型对比
9.1.2 存储容量和限制
// 存储配额信息
const StorageQuotas = {
sync: {
totalBytes: 102400, // 100KB
maxItems: 512, // 最大项目数
quotaBytesPerItem: 8192, // 单项8KB
maxWriteOperationsPerHour: 1800,
maxWriteOperationsPerMinute: 120
},
local: {
totalBytes: 5242880, // 5MB
maxItems: Infinity, // 无限制
quotaBytesPerItem: Infinity // 无限制
},
session: {
totalBytes: 10485760, // 10MB
maxItems: Infinity, // 无限制
quotaBytesPerItem: Infinity // 无限制
}
};
// 检查存储使用情况
async function checkStorageUsage() {
const syncUsage = await chrome.storage.sync.getBytesInUse();
const localUsage = await chrome.storage.local.getBytesInUse();
console.log('存储使用情况:');
console.log(`Sync: ${syncUsage}/${StorageQuotas.sync.totalBytes} bytes`);
console.log(`Local: ${localUsage}/${StorageQuotas.local.totalBytes} bytes`);
return {
sync: {
used: syncUsage,
total: StorageQuotas.sync.totalBytes,
percentage: (syncUsage / StorageQuotas.sync.totalBytes * 100).toFixed(2)
},
local: {
used: localUsage,
total: StorageQuotas.local.totalBytes,
percentage: (localUsage / StorageQuotas.local.totalBytes * 100).toFixed(2)
}
};
}9.2 Chrome Storage API
9.2.1 基础操作封装
// storage-manager.js - 存储管理器
class StorageManager {
constructor() {
this.cache = new Map(); // 内存缓存
this.syncQueue = []; // 同步队列
this.isOnline = navigator.onLine;
this.setupNetworkListener();
}
setupNetworkListener() {
window.addEventListener('online', () => {
this.isOnline = true;
this.processSyncQueue();
});
window.addEventListener('offline', () => {
this.isOnline = false;
});
}
// === Sync Storage 操作 ===
async setSyncData(key, value, options = {}) {
const { enableCache = true, priority = 'normal' } = options;
try {
// 验证数据大小
const dataSize = this.calculateDataSize({ [key]: value });
if (dataSize > StorageQuotas.sync.quotaBytesPerItem) {
throw new Error(`数据过大: ${dataSize} bytes,超过单项限制`);
}
// 保存到存储
await chrome.storage.sync.set({ [key]: value });
// 更新缓存
if (enableCache) {
this.cache.set(`sync:${key}`, value);
}
console.log(`Sync数据已保存: ${key}`);
return true;
} catch (error) {
console.error('保存Sync数据失败:', error);
// 离线时加入同步队列
if (!this.isOnline) {
this.syncQueue.push({
type: 'set',
storage: 'sync',
key,
value,
timestamp: Date.now(),
priority
});
console.log('已加入同步队列,等待网络连接');
}
throw error;
}
}
async getSyncData(key, defaultValue = null, useCache = true) {
try {
// 先检查缓存
if (useCache && this.cache.has(`sync:${key}`)) {
return this.cache.get(`sync:${key}`);
}
// 从存储读取
const result = await chrome.storage.sync.get(key);
const value = result[key] !== undefined ? result[key] : defaultValue;
// 更新缓存
if (useCache && value !== null) {
this.cache.set(`sync:${key}`, value);
}
return value;
} catch (error) {
console.error('获取Sync数据失败:', error);
return defaultValue;
}
}
async removeSyncData(key) {
try {
await chrome.storage.sync.remove(key);
this.cache.delete(`sync:${key}`);
console.log(`Sync数据已删除: ${key}`);
return true;
} catch (error) {
console.error('删除Sync数据失败:', error);
throw error;
}
}
// === Local Storage 操作 ===
async setLocalData(key, value, options = {}) {
const {
enableCache = true,
compress = false,
ttl = null // 生存时间(秒)
} = options;
try {
let dataToStore = value;
// 添加TTL信息
if (ttl) {
dataToStore = {
data: value,
timestamp: Date.now(),
ttl: ttl * 1000 // 转换为毫秒
};
}
// 数据压缩
if (compress && typeof value === 'string') {
dataToStore = this.compressString(dataToStore);
}
await chrome.storage.local.set({ [key]: dataToStore });
// 更新缓存
if (enableCache) {
this.cache.set(`local:${key}`, value);
}
console.log(`Local数据已保存: ${key}`);
return true;
} catch (error) {
console.error('保存Local数据失败:', error);
throw error;
}
}
async getLocalData(key, defaultValue = null, useCache = true) {
try {
// 先检查缓存
if (useCache && this.cache.has(`local:${key}`)) {
return this.cache.get(`local:${key}`);
}
const result = await chrome.storage.local.get(key);
let value = result[key];
if (value === undefined) {
return defaultValue;
}
// 检查TTL
if (value && typeof value === 'object' && value.ttl) {
const now = Date.now();
if (now - value.timestamp > value.ttl) {
// 数据已过期,删除并返回默认值
await this.removeLocalData(key);
return defaultValue;
}
value = value.data;
}
// 解压缩
if (typeof value === 'object' && value._compressed) {
value = this.decompressString(value);
}
// 更新缓存
if (useCache && value !== null) {
this.cache.set(`local:${key}`, value);
}
return value;
} catch (error) {
console.error('获取Local数据失败:', error);
return defaultValue;
}
}
async removeLocalData(key) {
try {
await chrome.storage.local.remove(key);
this.cache.delete(`local:${key}`);
console.log(`Local数据已删除: ${key}`);
return true;
} catch (error) {
console.error('删除Local数据失败:', error);
throw error;
}
}
// === Session Storage 操作 ===
async setSessionData(key, value) {
try {
await chrome.storage.session.set({ [key]: value });
this.cache.set(`session:${key}`, value);
console.log(`Session数据已保存: ${key}`);
return true;
} catch (error) {
console.error('保存Session数据失败:', error);
throw error;
}
}
async getSessionData(key, defaultValue = null) {
try {
// 先检查缓存
if (this.cache.has(`session:${key}`)) {
return this.cache.get(`session:${key}`);
}
const result = await chrome.storage.session.get(key);
const value = result[key] !== undefined ? result[key] : defaultValue;
// 更新缓存
if (value !== null) {
this.cache.set(`session:${key}`, value);
}
return value;
} catch (error) {
console.error('获取Session数据失败:', error);
return defaultValue;
}
}
// === 批量操作 ===
async setBulkData(data, storageType = 'local') {
const operations = [];
for (const [key, value] of Object.entries(data)) {
switch (storageType) {
case 'sync':
operations.push(this.setSyncData(key, value));
break;
case 'local':
operations.push(this.setLocalData(key, value));
break;
case 'session':
operations.push(this.setSessionData(key, value));
break;
}
}
try {
await Promise.all(operations);
console.log(`批量保存完成: ${Object.keys(data).length} 项`);
return true;
} catch (error) {
console.error('批量保存失败:', error);
throw error;
}
}
async getBulkData(keys, storageType = 'local', defaultValues = {}) {
try {
const result = {};
for (const key of keys) {
const defaultValue = defaultValues[key] || null;
switch (storageType) {
case 'sync':
result[key] = await this.getSyncData(key, defaultValue);
break;
case 'local':
result[key] = await this.getLocalData(key, defaultValue);
break;
case 'session':
result[key] = await this.getSessionData(key, defaultValue);
break;
}
}
return result;
} catch (error) {
console.error('批量获取失败:', error);
throw error;
}
}
// === 工具方法 ===
calculateDataSize(data) {
return new Blob([JSON.stringify(data)]).size;
}
compressString(str) {
// 简单的压缩实现(实际项目中应使用专业压缩库)
try {
const compressed = btoa(unescape(encodeURIComponent(str)));
return {
_compressed: true,
data: compressed,
originalSize: str.length,
compressedSize: compressed.length
};
} catch (error) {
console.warn('压缩失败,使用原始数据');
return str;
}
}
decompressString(compressedObj) {
try {
if (compressedObj._compressed) {
return decodeURIComponent(escape(atob(compressedObj.data)));
}
return compressedObj;
} catch (error) {
console.warn('解压失败,返回原始数据');
return compressedObj;
}
}
async processSyncQueue() {
if (this.syncQueue.length === 0) return;
console.log(`处理同步队列: ${this.syncQueue.length} 项`);
// 按优先级排序
this.syncQueue.sort((a, b) => {
const priorityOrder = { high: 0, normal: 1, low: 2 };
return priorityOrder[a.priority] - priorityOrder[b.priority];
});
const failedItems = [];
for (const item of this.syncQueue) {
try {
if (item.type === 'set') {
await this.setSyncData(item.key, item.value, { enableCache: false });
}
} catch (error) {
console.error('同步队列项目失败:', error);
failedItems.push(item);
}
}
// 更新队列,保留失败的项目
this.syncQueue = failedItems;
if (failedItems.length > 0) {
console.warn(`${failedItems.length} 个项目同步失败`);
}
}
// 清理缓存
clearCache() {
this.cache.clear();
console.log('缓存已清理');
}
// 获取缓存统计
getCacheStats() {
return {
size: this.cache.size,
keys: Array.from(this.cache.keys())
};
}
}
// 单例模式
const storageManager = new StorageManager();
window.storageManager = storageManager; // 开发调试用9.2.2 数据模型设计
// data-models.js - 数据模型定义
class DataModel {
constructor(data = {}) {
this.id = data.id || this.generateId();
this.createdAt = data.createdAt || Date.now();
this.updatedAt = data.updatedAt || Date.now();
this.version = data.version || 1;
}
generateId() {
return `${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
update(newData) {
Object.assign(this, newData);
this.updatedAt = Date.now();
this.version += 1;
}
toJSON() {
return {
id: this.id,
createdAt: this.createdAt,
updatedAt: this.updatedAt,
version: this.version,
...this.getData()
};
}
getData() {
// 子类实现
return {};
}
}
// 用户设置模型
class UserSettings extends DataModel {
constructor(data = {}) {
super(data);
this.theme = data.theme || 'light';
this.language = data.language || 'zh-CN';
this.notifications = data.notifications !== undefined ? data.notifications : true;
this.autoSave = data.autoSave !== undefined ? data.autoSave : true;
this.fontSize = data.fontSize || 14;
this.shortcuts = data.shortcuts || {};
}
getData() {
return {
theme: this.theme,
language: this.language,
notifications: this.notifications,
autoSave: this.autoSave,
fontSize: this.fontSize,
shortcuts: this.shortcuts
};
}
static getStorageKey() {
return 'userSettings';
}
async save() {
return await storageManager.setSyncData(
UserSettings.getStorageKey(),
this.toJSON()
);
}
static async load() {
const data = await storageManager.getSyncData(
UserSettings.getStorageKey(),
{}
);
return new UserSettings(data);
}
}
// 用户数据模型
class UserProfile extends DataModel {
constructor(data = {}) {
super(data);
this.username = data.username || '';
this.email = data.email || '';
this.avatar = data.avatar || '';
this.preferences = data.preferences || {};
this.statistics = data.statistics || {
clickCount: 0,
activeTime: 0,
lastLogin: null
};
}
getData() {
return {
username: this.username,
email: this.email,
avatar: this.avatar,
preferences: this.preferences,
statistics: this.statistics
};
}
incrementClickCount() {
this.statistics.clickCount += 1;
this.update({});
}
updateActiveTime(seconds) {
this.statistics.activeTime += seconds;
this.update({});
}
static getStorageKey() {
return 'userProfile';
}
async save() {
return await storageManager.setSyncData(
UserProfile.getStorageKey(),
this.toJSON()
);
}
static async load() {
const data = await storageManager.getSyncData(
UserProfile.getStorageKey(),
{}
);
return new UserProfile(data);
}
}
// 缓存数据模型
class CacheEntry extends DataModel {
constructor(data = {}) {
super(data);
this.key = data.key;
this.value = data.value;
this.ttl = data.ttl || 3600000; // 1小时默认TTL
this.accessCount = data.accessCount || 0;
this.lastAccessed = data.lastAccessed || Date.now();
}
isExpired() {
return Date.now() - this.createdAt > this.ttl;
}
access() {
this.accessCount += 1;
this.lastAccessed = Date.now();
}
getData() {
return {
key: this.key,
value: this.value,
ttl: this.ttl,
accessCount: this.accessCount,
lastAccessed: this.lastAccessed
};
}
static getStorageKey(key) {
return `cache:${key}`;
}
async save() {
return await storageManager.setLocalData(
CacheEntry.getStorageKey(this.key),
this.toJSON(),
{ ttl: this.ttl / 1000 } // 转换为秒
);
}
static async load(key) {
const data = await storageManager.getLocalData(
CacheEntry.getStorageKey(key),
null
);
if (!data) return null;
const entry = new CacheEntry(data);
if (entry.isExpired()) {
await storageManager.removeLocalData(CacheEntry.getStorageKey(key));
return null;
}
return entry;
}
}9.2.3 Python模拟存储系统
# storage_system.py - 模拟Chrome扩展存储系统
import json
import time
import gzip
import pickle
from typing import Dict, Any, Optional, List, Union
from datetime import datetime, timedelta
from dataclasses import dataclass, asdict
from enum import Enum
class StorageType(Enum):
SYNC = "sync"
LOCAL = "local"
SESSION = "session"
@dataclass
class StorageQuota:
total_bytes: int
max_items: int
quota_bytes_per_item: int
max_write_operations_per_hour: int = None
max_write_operations_per_minute: int = None
class ChromeStorageEmulator:
"""模拟Chrome扩展存储系统"""
QUOTAS = {
StorageType.SYNC: StorageQuota(
total_bytes=102400, # 100KB
max_items=512,
quota_bytes_per_item=8192,
max_write_operations_per_hour=1800,
max_write_operations_per_minute=120
),
StorageType.LOCAL: StorageQuota(
total_bytes=5242880, # 5MB
max_items=float('inf'),
quota_bytes_per_item=float('inf')
),
StorageType.SESSION: StorageQuota(
total_bytes=10485760, # 10MB
max_items=float('inf'),
quota_bytes_per_item=float('inf')
)
}
def __init__(self):
self.storages = {
StorageType.SYNC: {},
StorageType.LOCAL: {},
StorageType.SESSION: {}
}
self.write_operations = {
StorageType.SYNC: []
}
self.cache = {}
self.is_online = True
def calculate_size(self, data: Any) -> int:
"""计算数据大小"""
return len(json.dumps(data, ensure_ascii=False).encode('utf-8'))
def check_quota(self, storage_type: StorageType, key: str, value: Any) -> bool:
"""检查存储配额"""
quota = self.QUOTAS[storage_type]
current_storage = self.storages[storage_type]
# 检查总项目数
if len(current_storage) >= quota.max_items and key not in current_storage:
raise Exception(f"存储项目数超过限制: {quota.max_items}")
# 检查单项大小
item_size = self.calculate_size(value)
if item_size > quota.quota_bytes_per_item:
raise Exception(f"单项数据过大: {item_size} bytes,限制: {quota.quota_bytes_per_item}")
# 检查总大小
total_size = sum(self.calculate_size(v) for v in current_storage.values())
if key in current_storage:
total_size -= self.calculate_size(current_storage[key])
total_size += item_size
if total_size > quota.total_bytes:
raise Exception(f"总存储容量超限: {total_size} bytes,限制: {quota.total_bytes}")
return True
def check_write_operations(self, storage_type: StorageType) -> bool:
"""检查写入操作频率限制"""
if storage_type != StorageType.SYNC:
return True
now = time.time()
operations = self.write_operations[storage_type]
# 清理过期的操作记录
operations[:] = [op for op in operations if now - op < 3600] # 1小时内
# 检查小时限制
quota = self.QUOTAS[storage_type]
if len(operations) >= quota.max_write_operations_per_hour:
raise Exception("写入操作超过小时限制")
# 检查分钟限制
recent_operations = [op for op in operations if now - op < 60] # 1分钟内
if len(recent_operations) >= quota.max_write_operations_per_minute:
raise Exception("写入操作超过分钟限制")
return True
def set(self, storage_type: StorageType, key: str, value: Any) -> bool:
"""设置存储数据"""
try:
self.check_quota(storage_type, key, value)
self.check_write_operations(storage_type)
# 模拟网络延迟
if storage_type == StorageType.SYNC and not self.is_online:
raise Exception("网络离线,无法同步数据")
self.storages[storage_type][key] = value
# 记录写入操作
if storage_type == StorageType.SYNC:
self.write_operations[storage_type].append(time.time())
# 更新缓存
cache_key = f"{storage_type.value}:{key}"
self.cache[cache_key] = value
print(f"数据已保存到 {storage_type.value}: {key}")
return True
except Exception as e:
print(f"保存数据失败: {e}")
raise
def get(self, storage_type: StorageType, key: str, default_value: Any = None) -> Any:
"""获取存储数据"""
try:
# 先检查缓存
cache_key = f"{storage_type.value}:{key}"
if cache_key in self.cache:
return self.cache[cache_key]
# 从存储获取
value = self.storages[storage_type].get(key, default_value)
# 更新缓存
if value is not None:
self.cache[cache_key] = value
return value
except Exception as e:
print(f"获取数据失败: {e}")
return default_value
def remove(self, storage_type: StorageType, key: str) -> bool:
"""删除存储数据"""
try:
if key in self.storages[storage_type]:
del self.storages[storage_type][key]
# 清理缓存
cache_key = f"{storage_type.value}:{key}"
self.cache.pop(cache_key, None)
print(f"数据已删除: {storage_type.value}:{key}")
return True
return False
except Exception as e:
print(f"删除数据失败: {e}")
return False
def clear(self, storage_type: StorageType) -> bool:
"""清空指定类型的存储"""
try:
self.storages[storage_type].clear()
# 清理相关缓存
cache_keys_to_remove = [
k for k in self.cache.keys()
if k.startswith(f"{storage_type.value}:")
]
for key in cache_keys_to_remove:
del self.cache[key]
print(f"{storage_type.value} 存储已清空")
return True
except Exception as e:
print(f"清空存储失败: {e}")
return False
def get_bytes_in_use(self, storage_type: StorageType, keys: List[str] = None) -> int:
"""获取存储使用的字节数"""
storage = self.storages[storage_type]
if keys:
# 计算特定键的大小
total_size = 0
for key in keys:
if key in storage:
total_size += self.calculate_size(storage[key])
return total_size
else:
# 计算整个存储的大小
return sum(self.calculate_size(v) for v in storage.values())
def get_all(self, storage_type: StorageType) -> Dict[str, Any]:
"""获取所有数据"""
return self.storages[storage_type].copy()
def get_keys(self, storage_type: StorageType) -> List[str]:
"""获取所有键"""
return list(self.storages[storage_type].keys())
def set_online_status(self, is_online: bool):
"""设置在线状态"""
self.is_online = is_online
print(f"网络状态: {'在线' if is_online else '离线'}")
def get_storage_info(self) -> Dict[str, Dict[str, Any]]:
"""获取存储信息"""
info = {}
for storage_type in StorageType:
quota = self.QUOTAS[storage_type]
used_bytes = self.get_bytes_in_use(storage_type)
used_items = len(self.storages[storage_type])
info[storage_type.value] = {
'used_bytes': used_bytes,
'total_bytes': quota.total_bytes,
'used_percentage': (used_bytes / quota.total_bytes * 100) if quota.total_bytes != float('inf') else 0,
'used_items': used_items,
'max_items': quota.max_items if quota.max_items != float('inf') else None,
'available_bytes': quota.total_bytes - used_bytes if quota.total_bytes != float('inf') else None
}
return info
def compress_data(self, data: Any) -> bytes:
"""压缩数据"""
serialized = pickle.dumps(data)
compressed = gzip.compress(serialized)
return compressed
def decompress_data(self, compressed_data: bytes) -> Any:
"""解压数据"""
decompressed = gzip.decompress(compressed_data)
return pickle.loads(decompressed)
class StorageManager:
"""高级存储管理器"""
def __init__(self):
self.storage = ChromeStorageEmulator()
self.sync_queue = []
self.ttl_cache = {}
def set_with_ttl(self, storage_type: StorageType, key: str, value: Any, ttl_seconds: int):
"""设置带TTL的数据"""
expiry_time = time.time() + ttl_seconds
wrapped_value = {
'data': value,
'expiry': expiry_time,
'created': time.time()
}
self.storage.set(storage_type, key, wrapped_value)
self.ttl_cache[f"{storage_type.value}:{key}"] = expiry_time
def get_with_ttl(self, storage_type: StorageType, key: str, default_value: Any = None) -> Any:
"""获取带TTL的数据"""
cache_key = f"{storage_type.value}:{key}"
# 检查TTL缓存
if cache_key in self.ttl_cache:
if time.time() > self.ttl_cache[cache_key]:
# 数据已过期
self.storage.remove(storage_type, key)
del self.ttl_cache[cache_key]
return default_value
wrapped_value = self.storage.get(storage_type, key)
if not wrapped_value:
return default_value
# 检查是否为TTL包装的数据
if isinstance(wrapped_value, dict) and 'expiry' in wrapped_value:
if time.time() > wrapped_value['expiry']:
# 数据已过期
self.storage.remove(storage_type, key)
self.ttl_cache.pop(cache_key, None)
return default_value
return wrapped_value['data']
return wrapped_value
def set_bulk(self, storage_type: StorageType, data_dict: Dict[str, Any]) -> List[str]:
"""批量设置数据"""
failed_keys = []
for key, value in data_dict.items():
try:
self.storage.set(storage_type, key, value)
except Exception as e:
print(f"批量设置失败 {key}: {e}")
failed_keys.append(key)
return failed_keys
def get_bulk(self, storage_type: StorageType, keys: List[str], default_values: Dict[str, Any] = None) -> Dict[str, Any]:
"""批量获取数据"""
default_values = default_values or {}
result = {}
for key in keys:
default_value = default_values.get(key)
result[key] = self.storage.get(storage_type, key, default_value)
return result
def cleanup_expired(self):
"""清理过期数据"""
current_time = time.time()
expired_keys = []
for cache_key, expiry_time in self.ttl_cache.items():
if current_time > expiry_time:
storage_type_str, key = cache_key.split(':', 1)
storage_type = StorageType(storage_type_str)
self.storage.remove(storage_type, key)
expired_keys.append(cache_key)
for key in expired_keys:
del self.ttl_cache[key]
if expired_keys:
print(f"清理了 {len(expired_keys)} 个过期项目")
def export_data(self, storage_type: StorageType, filename: str = None):
"""导出存储数据"""
if not filename:
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
filename = f"{storage_type.value}_export_{timestamp}.json"
data = self.storage.get_all(storage_type)
export_data = {
'type': storage_type.value,
'exported_at': datetime.now().isoformat(),
'data': data
}
try:
with open(filename, 'w', encoding='utf-8') as f:
json.dump(export_data, f, indent=2, ensure_ascii=False)
print(f"数据已导出到: {filename}")
return filename
except Exception as e:
print(f"导出失败: {e}")
return None
def import_data(self, filename: str, storage_type: StorageType = None) -> bool:
"""导入存储数据"""
try:
with open(filename, 'r', encoding='utf-8') as f:
import_data = json.load(f)
target_storage_type = storage_type or StorageType(import_data['type'])
data = import_data['data']
failed_keys = self.set_bulk(target_storage_type, data)
if failed_keys:
print(f"导入完成,{len(failed_keys)} 项失败: {failed_keys}")
else:
print("数据导入成功")
return len(failed_keys) == 0
except Exception as e:
print(f"导入失败: {e}")
return False
# 使用示例
def demo_storage_system():
"""演示存储系统功能"""
print("=== Chrome扩展存储系统演示 ===\n")
# 创建存储管理器
manager = StorageManager()
# 1. 基础存储操作
print("1. 基础存储操作:")
manager.storage.set(StorageType.SYNC, 'username', 'john_doe')
manager.storage.set(StorageType.LOCAL, 'large_data', {'items': list(range(100))})
username = manager.storage.get(StorageType.SYNC, 'username')
large_data = manager.storage.get(StorageType.LOCAL, 'large_data')
print(f" 用户名: {username}")
print(f" 大数据项目数: {len(large_data['items'])}")
# 2. TTL存储
print("\n2. TTL存储:")
manager.set_with_ttl(StorageType.LOCAL, 'temp_token', 'abc123', 5) # 5秒后过期
token = manager.get_with_ttl(StorageType.LOCAL, 'temp_token')
print(f" 临时令牌: {token}")
time.sleep(2)
print(" 2秒后...")
token = manager.get_with_ttl(StorageType.LOCAL, 'temp_token')
print(f" 临时令牌: {token}")
# 3. 批量操作
print("\n3. 批量操作:")
bulk_data = {
'setting1': True,
'setting2': 'value2',
'setting3': {'nested': 'data'}
}
failed_keys = manager.set_bulk(StorageType.SYNC, bulk_data)
retrieved_data = manager.get_bulk(StorageType.SYNC, list(bulk_data.keys()))
print(f" 批量设置失败键: {failed_keys}")
print(f" 批量获取结果: {len(retrieved_data)} 项")
# 4. 存储信息
print("\n4. 存储使用情况:")
info = manager.storage.get_storage_info()
for storage_type, stats in info.items():
print(f" {storage_type}:")
print(f" 使用: {stats['used_bytes']} bytes ({stats['used_percentage']:.1f}%)")
print(f" 项目: {stats['used_items']}")
# 5. 导出/导入测试
print("\n5. 导出数据:")
export_file = manager.export_data(StorageType.SYNC)
if export_file:
print(f" 导出文件: {export_file}")
print("\n=== 演示完成 ===")
# 运行演示(注释掉实际执行)
# demo_storage_system()9.3 IndexedDB高级存储
9.3.1 IndexedDB封装
// indexeddb-manager.js - IndexedDB高级封装
class IndexedDBManager {
constructor(dbName, version = 1) {
this.dbName = dbName;
this.version = version;
this.db = null;
this.objectStores = new Map();
}
// 定义对象存储
defineStore(storeName, options = {}) {
const {
keyPath = 'id',
autoIncrement = false,
indexes = []
} = options;
this.objectStores.set(storeName, {
keyPath,
autoIncrement,
indexes
});
}
// 初始化数据库
async init() {
return new Promise((resolve, reject) => {
const request = indexedDB.open(this.dbName, this.version);
request.onerror = () => {
reject(new Error(`数据库打开失败: ${request.error}`));
};
request.onsuccess = (event) => {
this.db = event.target.result;
console.log(`数据库 ${this.dbName} 初始化成功`);
resolve(this.db);
};
request.onupgradeneeded = (event) => {
const db = event.target.result;
console.log(`数据库升级: 版本 ${event.oldVersion} -> ${event.newVersion}`);
// 创建对象存储
for (const [storeName, config] of this.objectStores) {
if (!db.objectStoreNames.contains(storeName)) {
const objectStore = db.createObjectStore(storeName, {
keyPath: config.keyPath,
autoIncrement: config.autoIncrement
});
// 创建索引
config.indexes.forEach(index => {
objectStore.createIndex(
index.name,
index.keyPath,
index.options || {}
);
});
console.log(`对象存储 ${storeName} 创建成功`);
}
}
};
});
}
// 执行事务
async transaction(storeNames, mode = 'readonly', operation) {
if (!this.db) {
throw new Error('数据库未初始化');
}
return new Promise((resolve, reject) => {
const transaction = this.db.transaction(storeNames, mode);
transaction.oncomplete = () => resolve(transaction);
transaction.onerror = () => reject(new Error(`事务失败: ${transaction.error}`));
transaction.onabort = () => reject(new Error('事务被中止'));
try {
const result = operation(transaction);
if (result instanceof Promise) {
result.catch(reject);
}
} catch (error) {
reject(error);
}
});
}
// 添加数据
async add(storeName, data) {
return this.transaction([storeName], 'readwrite', (transaction) => {
const objectStore = transaction.objectStore(storeName);
const request = objectStore.add(data);
return new Promise((resolve, reject) => {
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(new Error(`添加数据失败: ${request.error}`));
});
});
}
// 更新数据
async put(storeName, data) {
return this.transaction([storeName], 'readwrite', (transaction) => {
const objectStore = transaction.objectStore(storeName);
const request = objectStore.put(data);
return new Promise((resolve, reject) => {
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(new Error(`更新数据失败: ${request.error}`));
});
});
}
// 获取数据
async get(storeName, key) {
return this.transaction([storeName], 'readonly', (transaction) => {
const objectStore = transaction.objectStore(storeName);
const request = objectStore.get(key);
return new Promise((resolve, reject) => {
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(new Error(`获取数据失败: ${request.error}`));
});
});
}
// 获取所有数据
async getAll(storeName, query = null, count = null) {
return this.transaction([storeName], 'readonly', (transaction) => {
const objectStore = transaction.objectStore(storeName);
const request = objectStore.getAll(query, count);
return new Promise((resolve, reject) => {
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(new Error(`获取所有数据失败: ${request.error}`));
});
});
}
// 删除数据
async delete(storeName, key) {
return this.transaction([storeName], 'readwrite', (transaction) => {
const objectStore = transaction.objectStore(storeName);
const request = objectStore.delete(key);
return new Promise((resolve, reject) => {
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(new Error(`删除数据失败: ${request.error}`));
});
});
}
// 清空存储
async clear(storeName) {
return this.transaction([storeName], 'readwrite', (transaction) => {
const objectStore = transaction.objectStore(storeName);
const request = objectStore.clear();
return new Promise((resolve, reject) => {
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(new Error(`清空存储失败: ${request.error}`));
});
});
}
// 通过索引查询
async queryByIndex(storeName, indexName, query) {
return this.transaction([storeName], 'readonly', (transaction) => {
const objectStore = transaction.objectStore(storeName);
const index = objectStore.index(indexName);
const request = index.getAll(query);
return new Promise((resolve, reject) => {
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(new Error(`索引查询失败: ${request.error}`));
});
});
}
// 游标遍历
async forEach(storeName, callback, keyRange = null) {
return this.transaction([storeName], 'readonly', (transaction) => {
const objectStore = transaction.objectStore(storeName);
const request = objectStore.openCursor(keyRange);
return new Promise((resolve, reject) => {
request.onsuccess = (event) => {
const cursor = event.target.result;
if (cursor) {
const shouldContinue = callback(cursor.value, cursor.key);
if (shouldContinue !== false) {
cursor.continue();
} else {
resolve();
}
} else {
resolve();
}
};
request.onerror = () => reject(new Error(`游标遍历失败: ${request.error}`));
});
});
}
// 批量操作
async batch(storeName, operations) {
return this.transaction([storeName], 'readwrite', async (transaction) => {
const objectStore = transaction.objectStore(storeName);
const promises = [];
for (const operation of operations) {
const { type, data, key } = operation;
let request;
switch (type) {
case 'add':
request = objectStore.add(data);
break;
case 'put':
request = objectStore.put(data);
break;
case 'delete':
request = objectStore.delete(key || data);
break;
default:
throw new Error(`未知操作类型: ${type}`);
}
promises.push(new Promise((resolve, reject) => {
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(new Error(`批量操作失败: ${request.error}`));
}));
}
return Promise.all(promises);
});
}
// 获取存储统计信息
async getStoreStats(storeName) {
return this.transaction([storeName], 'readonly', (transaction) => {
const objectStore = transaction.objectStore(storeName);
const countRequest = objectStore.count();
return new Promise((resolve, reject) => {
countRequest.onsuccess = () => {
resolve({
storeName,
count: countRequest.result,
keyPath: objectStore.keyPath,
autoIncrement: objectStore.autoIncrement,
indexes: Array.from(objectStore.indexNames)
});
};
countRequest.onerror = () => reject(new Error(`获取统计失败: ${countRequest.error}`));
});
});
}
// 导出数据
async exportStore(storeName) {
const allData = await this.getAll(storeName);
const stats = await this.getStoreStats(storeName);
return {
storeName,
exportDate: new Date().toISOString(),
stats,
data: allData
};
}
// 导入数据
async importStore(storeName, exportData, options = {}) {
const { clearFirst = false, skipExisting = false } = options;
if (clearFirst) {
await this.clear(storeName);
}
const operations = exportData.data.map(item => ({
type: skipExisting ? 'add' : 'put',
data: item
}));
try {
await this.batch(storeName, operations);
console.log(`导入完成: ${operations.length} 项数据`);
return true;
} catch (error) {
console.error('导入失败:', error);
return false;
}
}
// 关闭数据库
close() {
if (this.db) {
this.db.close();
this.db = null;
console.log('数据库连接已关闭');
}
}
}
// 应用数据管理器
class AppDataManager {
constructor() {
this.dbManager = new IndexedDBManager('ExtensionDB', 1);
this.isInitialized = false;
}
async init() {
if (this.isInitialized) return;
// 定义存储结构
this.dbManager.defineStore('users', {
keyPath: 'id',
autoIncrement: false,
indexes: [
{ name: 'email', keyPath: 'email', options: { unique: true } },
{ name: 'username', keyPath: 'username', options: { unique: false } },
{ name: 'createdAt', keyPath: 'createdAt', options: { unique: false } }
]
});
this.dbManager.defineStore('articles', {
keyPath: 'id',
autoIncrement: false,
indexes: [
{ name: 'title', keyPath: 'title', options: { unique: false } },
{ name: 'category', keyPath: 'category', options: { unique: false } },
{ name: 'publishedAt', keyPath: 'publishedAt', options: { unique: false } },
{ name: 'author', keyPath: 'authorId', options: { unique: false } }
]
});
this.dbManager.defineStore('cache', {
keyPath: 'key',
autoIncrement: false,
indexes: [
{ name: 'expiry', keyPath: 'expiry', options: { unique: false } },
{ name: 'category', keyPath: 'category', options: { unique: false } }
]
});
await this.dbManager.init();
this.isInitialized = true;
// 启动清理任务
this.startCleanupTasks();
console.log('应用数据管理器初始化完成');
}
// 用户管理
async createUser(userData) {
const user = {
id: this.generateId(),
...userData,
createdAt: Date.now(),
updatedAt: Date.now()
};
try {
await this.dbManager.add('users', user);
console.log('用户创建成功:', user.id);
return user;
} catch (error) {
if (error.message.includes('Key already exists')) {
throw new Error('用户已存在');
}
throw error;
}
}
async getUserByEmail(email) {
const users = await this.dbManager.queryByIndex('users', 'email', email);
return users.length > 0 ? users[0] : null;
}
async updateUser(userId, updates) {
const user = await this.dbManager.get('users', userId);
if (!user) {
throw new Error('用户不存在');
}
const updatedUser = {
...user,
...updates,
updatedAt: Date.now()
};
await this.dbManager.put('users', updatedUser);
return updatedUser;
}
// 文章管理
async createArticle(articleData) {
const article = {
id: this.generateId(),
...articleData,
createdAt: Date.now(),
updatedAt: Date.now()
};
await this.dbManager.add('articles', article);
console.log('文章创建成功:', article.id);
return article;
}
async getArticlesByCategory(category) {
return await this.dbManager.queryByIndex('articles', 'category', category);
}
async getRecentArticles(limit = 10) {
const allArticles = await this.dbManager.getAll('articles');
return allArticles
.sort((a, b) => b.publishedAt - a.publishedAt)
.slice(0, limit);
}
// 缓存管理
async setCache(key, value, ttl = 3600000) {
const cacheEntry = {
key,
value,
expiry: Date.now() + ttl,
category: 'general',
createdAt: Date.now()
};
await this.dbManager.put('cache', cacheEntry);
}
async getCache(key) {
const entry = await this.dbManager.get('cache', key);
if (!entry) return null;
if (Date.now() > entry.expiry) {
await this.dbManager.delete('cache', key);
return null;
}
return entry.value;
}
async clearExpiredCache() {
const now = Date.now();
let expiredCount = 0;
await this.dbManager.forEach('cache', async (entry, key) => {
if (entry.expiry < now) {
await this.dbManager.delete('cache', key);
expiredCount++;
}
});
console.log(`清理了 ${expiredCount} 个过期缓存项`);
return expiredCount;
}
// 定期清理任务
startCleanupTasks() {
// 每小时清理一次过期缓存
setInterval(() => {
this.clearExpiredCache().catch(console.error);
}, 3600000); // 1小时
console.log('清理任务已启动');
}
generateId() {
return `${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
// 数据统计
async getDataStats() {
const [userStats, articleStats, cacheStats] = await Promise.all([
this.dbManager.getStoreStats('users'),
this.dbManager.getStoreStats('articles'),
this.dbManager.getStoreStats('cache')
]);
return {
users: userStats,
articles: articleStats,
cache: cacheStats,
totalRecords: userStats.count + articleStats.count + cacheStats.count
};
}
// 数据备份
async backup() {
const [users, articles, cache] = await Promise.all([
this.dbManager.exportStore('users'),
this.dbManager.exportStore('articles'),
this.dbManager.exportStore('cache')
]);
const backup = {
version: '1.0',
timestamp: Date.now(),
stores: { users, articles, cache }
};
return backup;
}
async restore(backupData, options = {}) {
const { clearFirst = false } = options;
try {
for (const [storeName, storeData] of Object.entries(backupData.stores)) {
await this.dbManager.importStore(storeName, storeData, { clearFirst });
}
console.log('数据恢复完成');
return true;
} catch (error) {
console.error('数据恢复失败:', error);
return false;
}
}
}
// 全局实例
const appDataManager = new AppDataManager();
// 初始化
appDataManager.init().catch(console.error);
// 导出到全局作用域供其他文件使用
window.appDataManager = appDataManager;存储注意事项
- 配额管理: 合理使用存储配额,避免超限
- 数据同步: sync存储依赖网络,需要处理离线情况
- 性能优化: 大量数据操作时使用批量接口
- 数据迁移: 版本升级时处理数据结构变化
- 安全考虑: 敏感数据需要加密存储
最佳实践
- 根据数据特性选择合适的存储类型
- 实现数据分层和缓存策略
- 定期清理过期和无用数据
- 提供数据备份和恢复功能
- 监控存储使用情况和性能
总结
本章全面介绍了Chrome扩展的存储和数据管理技术,包括Chrome Storage API的各种用法、IndexedDB的高级应用,以及数据管理的最佳实践。合理的数据管理策略是构建可靠扩展的基础。下一章我们将学习Chrome APIs的深入应用,探索更多强大的浏览器功能。
