📦 Vue 中封装 IndexedDB 本地图片存储(支持 Blob)
🎯 功能目标:将图片 Blob 存入浏览器 IndexedDB,并在组件中便捷使用。
🧱 一、封装模块:indexedDbImage.ts
📁 路径:src/utils/indexedDbImage.ts
// utils/indexedDbImage.ts
import { openDB, type IDBPDatabase } from 'idb'
const DB_NAME = 'ImageCacheDB'
const STORE_NAME = 'images'
interface ImageRecord {
id: string // 图片唯一 ID,例如:uuid 或 hash
blob: Blob
createdAt: number
}
let db: IDBPDatabase | null = null
// 初始化图片库
export async function initImageDB() {
db = await openDB(DB_NAME, 1, {
upgrade(database) {
if (!database.objectStoreNames.contains(STORE_NAME)) {
const store = database.createObjectStore(STORE_NAME, {
keyPath: 'id',
})
store.createIndex('createdAt', 'createdAt')
}
},
})
}
// 添加图片(Blob)
export async function saveImage(id: string, blob: Blob) {
if (!db) throw new Error('DB 未初始化')
const tx = db.transaction(STORE_NAME, 'readwrite')
const store = tx.objectStore(STORE_NAME)
await store.put({ id, blob, createdAt: Date.now() })
await tx.done
}
// 获取图片 URL
export async function getImageURL(id: string): Promise<string | null> {
if (!db) throw new Error('DB 未初始化')
const tx = db.transaction(STORE_NAME, 'readonly')
const record = await tx.objectStore(STORE_NAME).get(id)
return record ? URL.createObjectURL(record.blob) : null
}
// 删除图片
export async function deleteImage(id: string) {
if (!db) throw new Error('DB 未初始化')
await db.transaction(STORE_NAME, 'readwrite').objectStore(STORE_NAME).delete(id)
}
// 获取所有图片 ID(可用于展示缩略图列表)
export async function getAllImageIds(): Promise<string[]> {
if (!db) throw new Error('DB 未初始化')
return await db.getAllKeys(STORE_NAME) as string[]
}
🚀 二、初始化:main.ts
import { createApp } from 'vue'
import App from './App.vue'
import { initImageDB } from './utils/indexedDbImage'
await initImageDB()
createApp(App).mount('#app')
🖼 三、组件中使用图片存储功能
📄 ImageUploader.vue
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { saveImage, getImageURL, getAllImageIds } from '@/utils/indexedDbImage'
const previewUrls = ref<string[]>([])
const handleUpload = async (event: Event) => {
const input = event.target as HTMLInputElement
const file = input.files?.[0]
if (file) {
const id = crypto.randomUUID() // 或 file.name
await saveImage(id, file)
await loadImages()
}
}
const loadImages = async () => {
const ids = await getAllImageIds()
const urls = await Promise.all(ids.map(id => getImageURL(id)))
previewUrls.value = urls.filter(Boolean) as string[]
}
onMounted(() => {
loadImages()
})
</script>
<template>
<div class="img-upload">
<h3>🖼 本地图片存储</h3>
<input type="file" accept="image/*" @change="handleUpload" />
<div class="img-preview">
<img v-for="url in previewUrls" :key="url" :src="url" alt="图片" />
</div>
</div>
</template>
<style scoped>
.img-preview {
display: flex;
gap: 12px;
flex-wrap: wrap;
}
.img-preview img {
width: 120px;
border-radius: 8px;
}
</style>
✅ 支持功能总结
功能 | 方法 |
---|---|
初始化图片数据库 | initImageDB() |
保存图片 Blob | saveImage(id, blob) |
获取图片 Blob URL | getImageURL(id) |
获取所有图片 ID | getAllImageIds() |
删除图片 | deleteImage(id) |
🔍 补充建议
- 若需支持过期自动清理,可在
createdAt
字段加定期清除逻辑。 - 可搭配
FileReader
实现缩略图生成。 - 若用于聊天缓存,可按消息
messageId
保存图片,支持富文本插图。
📌 总结
本地存储图片在以下场景极其有用:
- ✅ 离线应用(PWA)
- ✅ 聊天记录本地缓存图片
- ✅ 表单草稿本地图片暂存
- ✅ 提高首屏加载体验(缓存头像、缩略图)