使用indexDB储存展示本地文件
IndexDB 基础操作
IndexDB 是浏览器内置的一种 NoSQL 数据库,可以用于客户端存储数据。与传统的 cookie 和 localStorage 相比,它可以存储更大的数据量,而且支持复杂的查询。本文将介绍 IndexDB 的基础操作,包括创建数据库、创建对象仓库、增删改查数据等。
创建数据库
要创建 IndexDB 数据库,我们需要使用浏览器提供的 indexedDB 对象。首先,我们可以通过 open() 方法打开一个数据库,并指定数据库的名称和版本号:
// 数据库名称(自定义)
const dbName = "my-database";
// 数据库版本号(自定义)
const dbVersion = 1;
// 打开一个数据库并获取其reques对象
const request = indexedDB.open(dbName, dbVersion);
这里我们创建了一个名为 my-database,版本号为 1 的数据库。如果该数据库已存在,则打开它,否则将创建一个新的数据库。
接下来,我们需要监听 request 对象的 onerror、onsuccess 和 onupgradeneeded 事件。
方法名 | 释义 |
---|---|
onerror | 会在打开或创建数据库出错时触发 |
onsuccess | 会在成功打开或创建数据库时触发 |
onupgradeneeded | 则会在数据库版本发生变化时触发 |
request.onerror = (event) => {
console.error("打开数据库失败!", event.target.error);
};
request.onsuccess = (event) => {
// db 是数据库对象,可以简单理解为就是一个数据库,之后数据都是在这个数据库里储存的
const db = event.target.result;
console.log("打开数据库成功", db);
};
request.onupgradeneeded = (event) => {
const db = event.target.result;
console.log("数据库版本变更时会触发", db.version);
};
代码演示:
数据库创建成功后,我们在控制台中也可以观察到。
创建对象仓库
如果你明白mysql的话,那你一定对数据表非常了解。类似的,indexDB每一个数据库对象里都包含若干个对象仓库,每一个对象仓库都可以储存数据。使用前,你必须先创建这个对象仓库。
在 onupgradeneeded 事件中,我们可以创建一个或多个对象仓库(object store),并指定它们的键名、索引等信息。
// 数据库名称(自定义)
const dbName = "my-database";
// 数据库版本号(自定义)
const dbVersion = 1;
// 数据库对象
let db
// 打开一个数据库并获取其reques对象
const request = indexedDB.open(dbName, dbVersion);
// 数据库打开失败
request.onerror = (event) => {
console.error("打开数据库失败!", event.target.error);
};
// 数据库打开成功
request.onsuccess = (event) => {
// 数据库对象
db = event.target.result;
console.log("打开数据库成功", db);
};
// 数据库版本变更时候会触发
request.onupgradeneeded = (event) => {
// 数据库对象
db = event.target.result;
console.log("数据库版本变更", db.version);
// 创建一个名为 my-first-store 数据库对象仓库(创建一个数据表){ keyPath: "id" } 不用修改
const objectStore = db.createObjectStore("my-first-store", { keyPath: "id" });
};
上面的onupgradeneeded事件中,创建了一个名为 my-first-store 的对象仓库,并使用 id 作为主键。打开控制台刷新页面,可以观察到indexDB里面已经新增了my-database数据库,这个数据库内包含了我们刚刚创建的my-first-store对象仓库。
我们创建好了对象仓库,就可以在这个对象仓库内进行增删改查操作了。
数据增加
假设我们要添加一个名为 漩涡鸣人 的用户到 my-first-store 中,我们首先需要创建一个事务
事务创建
查询数据需要创建一个事务对象,使用事务对象可以对数据进行读取操作。可以通过调用IDBDatabase.transaction方法来创建一个事务:
// 创建事务
// 事务对象 指定表格名称和操作模式("只读:readonly"或"读写:readwrite")
const transaction = db.transaction(["my-first-store"], "readwrite")
获取对象存储
const objectStore = transaction.objectStore("my-first-store");
创建增加操作
// 要储存的信息
const user = { id: 1, name: "漩涡鸣人", email: "xunwomingren@example.com" };
// 储存user对象
const userRequest = objectStore.add(user);
// 储存成功
userRequest.onsuccess = (event) => {
console.log("User 添加成功");
};
// 储存失败
userRequest.onerror = (event) => {
console.error("User 添加失败", event.target.error);
};
完整代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<script type="text/javascript">
// 数据库名称(自定义)
const dbName = "my-database";
// 数据库版本号(自定义)
const dbVersion = 1;
// 数据库对象
let db
// 打开一个数据库并获取其reques对象
const request = indexedDB.open(dbName, dbVersion);
// 数据库打开失败
request.onerror = (event) => {
console.error("打开数据库失败!", event.target.error);
};
// 数据库打开成功
request.onsuccess = (event) => {
// 数据库对象
db = event.target.result;
console.log("打开数据库成功", db);
};
// 数据库版本变更时候会触发
request.onupgradeneeded = (event) => {
// 数据库对象
db = event.target.result;
console.log("数据库版本变更", db.version);
// 创建一个名为 my-first-store 数据库对象仓库(创建一个数据表){ keyPath: "id" } 不用修改
const objectStore = db.createObjectStore("my-first-store", { keyPath: "id" });
};
// 由于打开indexDB是异步的,为了演示方便,我们先加个定时器避免 db对象还没获取到值导致 报错
setTimeout(() => {
// 增加数据
// 要储存的信息
const user = { id: 1, name: "漩涡鸣人", email: "xunwomingren@example.com" };
// 通过事务(transaction)来打开 my-first-store 对象仓库,返回可操作的transaction实例
// 事务对象 指定表格名称和操作模式("只读"或"读写")
const transaction = db.transaction(["my-first-store"], "readwrite");
// 获取可以进行数据增删改查的objectStore对象
const objectStore = transaction.objectStore("my-first-store");
// 储存user对象
const userRequest = objectStore.add(user);
// 储存成功
userRequest.onsuccess = (event) => {
console.log("User 添加成功");
};
// 储存失败
userRequest.onerror = (event) => {
console.error("User 添加失败", event.target.error);
};
}, 1000)
</script>
</body>
</html>
上述代码创建了一个名为 user 的对象,它有 id、name 和 email三个属性。然后,我们通过事务(transaction)来打开 my-first-store ****对象仓库,并使用 add() 方法向其中添加该对象。如果添加成功,onsuccess 事件会被触发,否则会触发 onerror 事件。
除了 add() 方法,我们还可以使用 put() 方法更新或添加数据,使用 delete() 方法删除数据,以及使用 get() 方法查询数据。
数据更新
数据更新将add替换成put即可,其余操作一样
// 由于打开indexDB是异步的,为了演示方便,我们先加个定时器避免 db对象还没获取到值导致 报错
setTimeout(() => {
// 增加数据
// 要储存的信息
const user = { id: 1, name: "漩涡鸣人", email: null };
// 通过事务(transaction)来打开 my-first-store 对象仓库,返回可操作的transaction实例
// 事务对象 指定表格名称和操作模式("只读"或"读写")
const transaction = db.transaction(["my-first-store"], "readwrite");
// 获取可以进行数据增删改查的objectStore对象
const objectStore = transaction.objectStore("my-first-store");
// 储存user对象
const userRequest = objectStore.put(user);
// 储存成功
userRequest.onsuccess = (event) => {
console.log("User 更新成功");
};
// 储存失败
userRequest.onerror = (event) => {
console.error("User 更新失败", event.target.error);
};
}, 1000)
数据查询
数据查询可以查看单条数据,也可以查所有数据。
现在,数据库有两条数据:
通过openCursor方法查询所有数据
// 由于打开indexDB是异步的,为了演示方便,我们先加个定时器避免 db对象还没获取到值导致 报错
setTimeout(() => {
// 增加数据
// 要储存的信息
const user = { id: 2, name: "佐助", email: "zuozhun@example.com" };
// 通过事务(transaction)来打开 my-first-store 对象仓库,返回可操作的transaction实例
// 事务对象 指定表格名称和操作模式("只读"或"读写")
const transaction = db.transaction(["my-first-store"], "readwrite");
// 获取可以进行数据增删改查的objectStore对象
const objectStore = transaction.objectStore("my-first-store");
// 查询user对象
const userRequest = objectStore.openCursor();
// 查询成功
userRequest.onsuccess = (event) => {
const cursor = event.target.result;
if (cursor) {
console.log(cursor.value);
cursor.continue();
}
};
}, 1000)
注意,数据是被逐个打印出来的。
通过get方法查询单个数据
get方法需要传入主键(主键这里就是数据id,我们之前的代码已经指定: { keyPath: "id" })
// 由于打开indexDB是异步的,为了演示方便,我们先加个定时器避免 db对象还没获取到值导致 报错
setTimeout(() => {
// 增加数据
// 要储存的信息
const user = { id: 2, name: "佐助", email: "zuozhun@example.com" };
// 通过事务(transaction)来打开 my-first-store 对象仓库,返回可操作的transaction实例
// 事务对象 指定表格名称和操作模式("只读"或"读写")
const transaction = db.transaction(["my-first-store"], "readwrite");
// 获取可以进行数据增删改查的objectStore对象
const objectStore = transaction.objectStore("my-first-store");
// 查询user对象
const userRequest = objectStore.get(2);
// 查询成功
userRequest.onsuccess = (event) => {
console.log("数据查询成功:",event.target.result);
};
// 查询失败
userRequest.onerror = (event) => {
console.error("数据查询失败");
};
}, 1000)
数据删除delete
删除方法传入主键id即可
// 由于打开indexDB是异步的,为了演示方便,我们先加个定时器避免 db对象还没获取到值导致 报错
setTimeout(() => {
// 增加数据
// 要储存的信息
const user = { id: 2, name: "佐助", email: "zuozhun@example.com" };
// 通过事务(transaction)来打开 my-first-store 对象仓库,返回可操作的transaction实例
// 事务对象 指定表格名称和操作模式("只读"或"读写")
const transaction = db.transaction(["my-first-store"], "readwrite");
// 获取可以进行数据增删改查的objectStore对象
const objectStore = transaction.objectStore("my-first-store");
// 查询user对象
const userRequest = objectStore.delete(2);
// 删除成功
userRequest.onsuccess = (event) => {
console.log("数据删除成功:");
};
// 删除失败
userRequest.onerror = (event) => {
console.error("数据删除失败");
};
}, 1000)
流文件的获取与基础操作
如何将本地文件转换成流数据呢?
通过input标签获取数据信息
文件选取
html中,将input标签的type设置成file可以实现文件选取。
<body>
<input type="file" />
</body>
此时,input标签还有multiple、accept两个相关属性可选。
- multiple:每次是否可以选择多个文件
- accept:可选择的文件类型
<body>
<input type="file" multiple="multiple" accept=".pdf,.png" />
</body>
获取文件的数据信息
当我们选择文件后,会触发input标签上的changge事件,我们给该事件绑定我们自定义的处理事件,即可获取到选择的文件信息。如:
<body>
<input type="file" multiple="multiple" accept=".pdf,.png" id="inputFile" onchange="fileChange" />
</body>
<script type="text/javascript">
function fileChange(){
let fileList = document.getElementById("inputFile").files
console.log(fileList )
}
</script>
我们仔细观察控制台,可以发现文件选择后,浏览器返回了一个FileList对象,这个对象是一个伪数组,包含了每个文件的File对象。
File对象
什么是File对象呢?我们先看官网的定义:
文件(File)接口提供有关文件的信息,并允许网页中的 JavaScript 访问其内容。
简单来说,File是一个构造函数,我们控制台中的fileList[0] 对应的内容就是File的实例化对象。
验证下:
console.log(fileList[0].__proto__.constructor) // ƒ File() { [native code] }
File构造函数有5个属性:
属性名 | 属性含义 |
---|---|
File.lastModified | 返回当前 File 对象所引用文件最后修改时间 |
File.name | 返回当前 File 对象所引用文件的名字 |
File.size | 返回文件的大小 |
File.webkitRelativePath | 返回 File 相关的 path 或 URL |
File.type | 返回文件的MIME 类型developer.mozilla.org/zh-CN/docs/… |
File.lastModifiedDate | 返回当前 File 对象所引用文件最后修改时间的 Date 对象 |
fileList[0] 作为File构造函数的实例化对象,自然继承以上所有属性。
function fileChange(){
// 我们选择一个pdf文件
let fileList = document.getElementById("inputFile").files
console.log(fileList[0])
}
通过FileReader获取数据信息
我们可以理解为File对象是DOM接口和文件之间的一个桥梁,通过这个桥梁我们,我们拿到了文件,知道了它的名称、大小等信息。但如果我们想知道文件的具体内容是什么,就要借助其他接口实现了。
FileReader构造函数就是用来获取文件内容的。我们来看个示例:
<body>
<input type="file" multiple="multiple" id="inputFile" onchange="fileChange()" />
</body>
<script type="text/javascript">
function fileChange(){
//获取file对象(点击input,上传文件触发)
let fileList = document.getElementById("inputFile").files
//创建fileReader实例化对象
let fileReader = new FileReader();
//读取的文件或数据
fileReader.readAsDataURL(fileList[0])
//文件读取成功的回调
fileReader.onload = function(){
//fileReader.result 就是文件的的内容
console.log(fileReader.result)
}
}
</script>
从上述示例我们可以知道,通过fileReader的result属性,我们可以拿到文件的具体数据。我们来看看fileReader的其他属性与方法。
fileReader的属性与方法
主要的事件
事件名 | 事件触发条件 |
---|---|
FileReader.onabort | 该事件在读取操作被中断时触发。 |
FileReader.onerror | 该事件在读取操作发生错误时触发。 |
FileReader.onload | 该事件在读取操作完成时触发。 |
FileReader.onloadstart | 该事件在读取操作开始时触发。 |
FileReader.onloadend | 该事件在读取操作结束时(要么成功,要么失败)触发。 |
FileReader.onprogress | 该事件在读取文件时触发。 |
核心方法
方法名 | 方法内容 |
---|---|
FileReader.abort() | 中止读取操作。在返回时,readyState属性为DONE。 |
FileReader.readAsArrayBuffer() | 开始读取指定的 Blob中的内容, 一旦完成, result 属性中保存的将是被读取文件的 ArrayBuffer 数据对象. |
FileReader.readAsDataURL() | 开始读取指定的Blob中的内容。一旦完成,result属性中将包含一个data: URL格式的Base64字符串以表示所读取文件的内容。 |
FileReader.readAsText() | 开始读取指定的Blob中的内容。一旦完成,result属性中将包含一个字符串以表示所读取的文件内容。 |
注:这里的Blob也是一个文件属性的构造函数,File构造函数继承与Blob构造函数。File实例化对象拥有Blob上的所有属性与方法,也有一些属于自己的内置方法。
核心属性
只读属性, 文件的内容。该属性仅在读取操作完成后才有效,数据的格式取决于使用FileReader.readAsDataURL() 还是FileReader.readAsArrayBuffer()。
数据流转换常用方法
dataURL To Blob
function dataURLToBlob(dataurl) {
console.log('datarul', dataurl)
let arr = dataurl.split(','),
mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
return new Blob([u8arr], { type: mime })
}
base64 To Uint8Array
function base64ToUint8Array(base64String) {
const padding = '='.repeat((4 - base64String.length % 4) % 4);
const base64 = (base64String + padding)
.replace(/-/g, '+')
.replace(/_/g, '/');
const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}
arrayBuffer To Base64
function arrayBufferToBase64(buffer) {
var binary = '';
var bytes = new Uint8Array(buffer);
var len = bytes.byteLength;
for (var i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i]);
}
return window.btoa(binary);
}
indexDB实现文件上传并下载
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<input type="file" multiple="multiple" accept=".xls,.xlsx" id="inputFile" onchange="addfile()" />
<br />
<button onclick="downLoadFile()">下载文件</button>
</body>
<script type="text/javascript">
let fileName = "";
// dataUrl转流
function dataURLToBlob(dataurl) {
let arr = dataurl.split(","),
mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], { type: mime });
}
function downLoadFile() {
if (!db) return console.error("请稍后重试");
// 通过事务(transaction)来打开 my-first-store 对象仓库,返回可操作的transaction实例
// 事务对象 指定表格名称和操作模式("只读"或"读写")
const transaction = db.transaction(["my-first-store"], "readwrite");
// 获取可以进行数据增删改查的objectStore对象
const objectStore = transaction.objectStore("my-first-store");
// 获取要下载的user对象
const userRequest = objectStore.get(1);
// 查询成功
userRequest.onsuccess = (event) => {
console.log("数据查询成功:", event.target.result);
let { data } = event.target.result;
let b = dataURLToBlob(data); //拿到文件流下载对象
let url = window.URL.createObjectURL(b); //生成文件流下载链接
let a = document.createElement("a"); //创建一个a标签用来跳转
a.href = url; // a标签 href 赋值 链接
a.download = fileName; //设置下载文件的文件名和文件格式
document.body.appendChild(a); //将标签DOM,放置页面
a.click();
window.URL.revokeObjectURL(url); //释放 url 对象内存
document.body.removeChild(a); //移除标签节点
};
// 新增失败
userRequest.onerror = (event) => {
console.error("数据查询失败");
};
}
function addfile() {
//获取file对象(点击input,上传文件触发)
let fileList = document.getElementById("inputFile").files;
//创建fileReader实例化对象
let fileReader = new FileReader();
//读取的文件或数据
fileReader.readAsDataURL(fileList[0]);
fileName = fileList[0].name;
//文件读取成功的回调
fileReader.onload = function () {
//fileReader.result 就是文件的的内容
console.log(fileReader.result);
// 增加数据
// 要储存的信息
const user = { id: 1, name: fileName, data: fileReader.result };
if (!db) return console.error("请稍后重试");
// 通过事务(transaction)来打开 my-first-store 对象仓库,返回可操作的transaction实例
// 事务对象 指定表格名称和操作模式("只读"或"读写")
const transaction = db.transaction(["my-first-store"], "readwrite");
// 获取可以进行数据增删改查的objectStore对象
const objectStore = transaction.objectStore("my-first-store");
// 新增user对象
const userRequest = objectStore.add(user);
// 新增成功
userRequest.onsuccess = (event) => {
console.log("数据新增成功");
};
// 新增失败
userRequest.onerror = (event) => {
console.error("数据新增失败");
};
};
}
// 数据库名称(自定义)
const dbName = "my-database";
// 数据库版本号(自定义)
const dbVersion = 1;
// 数据库对象
let db;
// 打开一个数据库并获取其reques对象
const request = indexedDB.open(dbName, dbVersion);
// 数据库打开失败
request.onerror = (event) => {
console.error("打开数据库失败!", event.target.error);
};
// 数据库打开成功
request.onsuccess = (event) => {
// 数据库对象
db = event.target.result;
console.log("打开数据库成功", db);
};
// 数据库版本变更时候会触发
request.onupgradeneeded = (event) => {
// 数据库对象
db = event.target.result;
console.log("数据库版本变更", db.version);
// 创建一个名为 my-first-store 数据库对象仓库(创建一个数据表){ keyPath: "id" } 不用修改
const objectStore = db.createObjectStore("my-first-store", { keyPath: "id" });
};
</script>
</html>
转载自:https://juejin.cn/post/7221531861737357368