likes
comments
collection
share

使用indexDB储存展示本地文件

作者站长头像
站长
· 阅读数 38

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 对象的 onerroronsuccessonupgradeneeded 事件。

方法名释义
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);
};

代码演示:

使用indexDB储存展示本地文件

数据库创建成功后,我们在控制台中也可以观察到。

使用indexDB储存展示本地文件

创建对象仓库

如果你明白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对象仓库。

使用indexDB储存展示本地文件

我们创建好了对象仓库,就可以在这个对象仓库内进行增删改查操作了。

数据增加

假设我们要添加一个名为 漩涡鸣人 的用户到 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 的对象,它有 idname 和 email三个属性。然后,我们通过事务(transaction)来打开 my-first-store ****对象仓库,并使用 add() 方法向其中添加该对象。如果添加成功,onsuccess 事件会被触发,否则会触发 onerror 事件。

使用indexDB储存展示本地文件

除了 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)

数据查询

数据查询可以查看单条数据,也可以查所有数据。

现在,数据库有两条数据:

使用indexDB储存展示本地文件

通过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)

使用indexDB储存展示本地文件

注意,数据是被逐个打印出来的。

通过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>

使用indexDB储存展示本地文件

此时,input标签还有multipleaccept两个相关属性可选。

  • multiple:每次是否可以选择多个文件
  • accept:可选择的文件类型
<body>
		<input type="file"  multiple="multiple" accept=".pdf,.png" />
</body>

使用indexDB储存展示本地文件

获取文件的数据信息

当我们选择文件后,会触发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>

使用indexDB储存展示本地文件

我们仔细观察控制台,可以发现文件选择后,浏览器返回了一个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])
		}

使用indexDB储存展示本地文件

通过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>

使用indexDB储存展示本地文件

从上述示例我们可以知道,通过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.result

只读属性, 文件的内容。该属性仅在读取操作完成后才有效,数据的格式取决于使用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>

使用indexDB储存展示本地文件

转载自:https://juejin.cn/post/7221531861737357368
评论
请登录