前端本地存储数据库 IndexedDB 存储文件
介绍
IndexedDB 是一种浏览器底层 API,目前各浏览器都已支持,兼容性很好。
特点
- IndexedDB 内部采用对象仓库(object store)存放键值对数据。可以存储结构化克隆算法支持的任何对象。每一个数据记录都有对应的主键,主键是独一无二的,并且不能有重复。
- 使用索引实现对数据的高性能搜索。
- IndexedDB API 大部分都是异步的。不会通过返回值提供数据,而是要传递一个回调函数来获取返回值。
- 同大多数 Web 存储解决方案一样,IndexedDB 遵循同源策略。即可以访问相同域内存储的数据,但无法跨不同域访问数据。
- 可以存储文件、二进制对象。
- 在 Web Worker 中可用。
- IndexedDB 属于 NoSQL 和事务型面向对象数据库系统。对数据库的所有操作,都要通过事务完成。
IndexedDB 允许存储和检索用键索引的对象,它可以存储结构化克隆算法支持的任何对象。
结构化克隆
结构化克隆是由 HTML5 规范定义的用于复制复杂 JavaScript 对象的算法,例如 structuredClone()
函数的调用,Workers
的 postMessage()
方法或使用 IndexedDB
存储对象时在内部的调用。结构化克隆算法使 postMessage()
在 Worker
和主线程之间不再仅仅传输字符串类型,还可以传输包括 File
, Blob
, ArrayBuffer
, JSON
对象类型。支持的类型:
symbols
除外的所有原始类型。Boolean
String
原始类型包装对象。Array
Map
Set
Date
RegExp
内置对象。Blob
File
FileList
文件类型对象。ArrayBuffer
TypedArray
DataView
Buffer
类型对象。ImageBitmap
ImageData
图形对象。Object
仅包括普通对象,例如对象字面量。Error
,EvalError
,RangeError
,ReferenceError
,SyntaxError
,TypeError
,URIError
错误类型,其他类型错误name
属性会被转成Error
,message
属性会丢失。
IndexedDB 存储文件
下面我们通过在页面中定义一个文件上传按钮,选择图片文件后,会将文件保存到 IndexedDB 数据库中,当我们刷新页面,或关闭页面后,再次进入,会发现图片文件可以从 IndexedDB 数据库中读取,然后在页面中显示。
<body>
<label for="fileImg">上传图片</label>
<input type="file" id="fileImg" accept="image/*" hidden>
<p id="result"></p>
</body>
<script>
let db;
const request = indexedDB.open('the_file', 1);
request.onerror = console.log;
request.onsuccess = function(ev) {
console.log('打开');
db = request.result;
db.transaction(db.objectStoreNames).objectStore('files').openCursor().onsuccess = function(event) {
const cursor = event.target.result;
if (cursor) {
console.log(cursor.value);
const img = new Image();
img.src = URL.createObjectURL(cursor.value.file);
result.append(img);
cursor.continue();
}
}
};
request.onupgradeneeded = function(ev) {
console.log('升级');
db = request.result;
if (!db.objectStoreNames.contains('files')) {
const table = db.createObjectStore('files', { keyPath: 'id', autoIncrement: true });
table.createIndex('file', 'file', { unique: false });
}
};
fileImg.onchange = e => {
const file = fileImg.files[0];
const request = db.transaction(['files'], 'readwrite').objectStore('files').add({ file });
request.onsuccess = function(event) {
console.log('数据写入成功');
const img = new Image();
img.src = URL.createObjectURL(file);
result.append(img);
};
request.onerror = function(event) { console.log('数据写入失败'); }
};
</script>
<style>
label {
display: inline-block;
padding: 1em;
border-radius: .5em;
background: red;
color: white;
font-family: Arial, Helvetica, sans-serif
}
</style>