indexDB和WebSQL客户端离线存储大量数据

现如今,随着浏览器功能的日益强大,为了节省网络资源和增加用户体验,越来越多的数据被存储在客户端。常见的浏览器离线存储数据的方式有Cookie、webStorage、webSQL、indexDB、File system等,常用的cookie只能存储4kb的数据,而localStorage,sessionStorage只能存储2.5M-10M的数据,并且在代码中是同步执行的。

那么如果做可视化大屏或者聊天室这类应用,我们想在客户端存储大量数据,应该怎么做呢?

可以用到的技术有 WebSQL(官方已不再维护,现代浏览仍然保留其功能,仅作了解) 和 indexDB(支持存储大于250M的数据,并且是异步执行的。)

WebSQL【官方不再维护,仅作了解】

  • WebSQL数据库API并不是HTML5规范的一部分。但是它是一个独立的规范,引入了一组使用SQL操作客户端数据库的APIS

  • 最新版的safari,chrome和Opera浏览器都支持WebSQL

  • 目前,webSQL已经不再是W3C推荐的规范,官方已经不再维护,因为当前的SQL规范采用SQLite的SQL方言,而作为一个标准,这是不可接收的

  • webSQL使用SQL语言来进行操作,更像是一个关系型数据库,而indexDB则更像是一个非关系型数据库,这也是目前W3c强推的浏览器数据库解决方案。

indexDB和WebSQL客户端离线存储大量数据_第1张图片

在WebSQL中,有3个核心方法

  • openDatabase 这个方法使用现有的数据库或者新建的数据库创建一个数据库对象
  • transaction 这个方法让我们能够控制一个事务,以及基于这种情况执行提交或者回滚(事务失败就会发生回滚)
  • executeSql 这个方法用于执行实际的SQL查询

打开数据库

用openDatebase()方法来打开已经存在的数据库,如果数据库不存在,则会创建一个新得数据库,使用代码如下:

openDatabase()方法对应5个参数

  • 数据库名称
  • 版本号
  • 描述文本
  • 数据库大小
  • 创建回调
<body>
     <script>
		 var db=openDatabase('mydb','1.0','Test DB',2*1024*1024)
      script>
 body>

indexDB和WebSQL客户端离线存储大量数据_第2张图片

上面的代码表示,打开一个名为mydb的数据库,如果不存在,则会创建该数据库,版本号为1.0,大小为2M.

执行操作

使用代码database.transaction()创建表格和数据

database.transaction(function(tx){
    tx.executeSql()
})

executeSql(sqlStatement,arguments,callback,errorCallback)
该方法有4个参数

  • SQL语句
  • 参数
  • 执行SQL语句后的回调
  • 错误回调

静态插入数据:

<body>
     <script>
		// 返回数据库的实例
        var db=openDatabase('mydb','1.0','Test DB',2*1024*1024)
		// 实例上调用方法,接收一个回调函数
		db.transaction(function(tx){
		  //tx 是一个SQLTransaction对象,后面的所有操作都基于该对象。
		  //创建表格
		  tx.executeSql('CREATE TABLE IF NOT EXISTS STU(stuId unique,stuName)')
          //插入字段
		  tx.executeSql('INSERT INTO STU(stuId,stuName) values(1,"胡文浩")')
		  tx.executeSql('INSERT INTO STU(stuId,stuName) values(2,"张三")')
		})
      </script>
 </body>

indexDB和WebSQL客户端离线存储大量数据_第3张图片

动态插入数据:

indexDB和WebSQL客户端离线存储大量数据_第4张图片

 <body>
    <script>
        var stuName="李四"
        // 返回数据库的实例
        var db=openDatabase('mydb','1.0','Test DB',2*1024*1024)
		// 实例上调用方法,接收一个回调函数
		db.transaction(function(tx){
		    //tx 是一个SQLTransaction对象,后面的所有操作都基于该对象。
			//创建表格
		    tx.executeSql('CREATE TABLE IF NOT EXISTS STU(stuId unique,stuName)')
  //插入字段
			// tx.executeSql('INSERT INTO STU(stuId,stuName) values(1,"胡文浩")')
			// tx.executeSql('INSERT INTO STU(stuId,stuName) values(2,"张三")')
			//动态插入
			tx.executeSql('INSERT INTO STU(stuId,stuName) values(3,?)',[stuName])
		})

	</script>
 </body>

在上面的代码中,我们使用动态值得方式插入了一条数据,实例中的stuName是外部变量,excuteSql会映射数组参数中的每条目给"?"

注意:由于上一次操作已经插入了id为1和2的数据,所以这一次插入内容时,要把前两次的插入语句注释,否则插入操作不会成功。因为这里是一个事务,前面失败了会导致后面也失败,事务失败会发生回滚

读取数据

// 读取数据
db.transaction(function(tx){
    tx.executeSql('SELECT * FROM STU',[],function(tx,result){
        	console.log(result.rows) //拿到数据
        	msg=`

查询到的数据条数一共有:${result.rows.length}

`
content.innerHTML+=msg for(var i =0;i<result.rows.length;i++){ msg=`

${result.rows.item(i).stuName}

`
content.innerHTML+=msg } },null) })

indexDB和WebSQL客户端离线存储大量数据_第5张图片

删除数据

// 删除数据
var stuId=1
db.transaction(function(tx){
	 tx.executeSql('DELETE FROM STU WHERE stuId=?',[stuId])
})

indexDB和WebSQL客户端离线存储大量数据_第6张图片

修改数据

要修改数据也是使用SQL中的语法,同样也支持动态指定的方式

var stuName="寒冰"
db.transaction(function(tx){
	tx.executeSql('UPDATE STU SET stuName=? WHERE stuId=2',[stuName])
})

indexDB和WebSQL客户端离线存储大量数据_第7张图片

indexedDB

indexedDB 简介

随着浏览器的功能不断增强,越来越多的网站开始考虑,将大量的数据存储在客户端,这样可以减少服务端获取数据,直接从本地获取数据。

现有的浏览器数据存储方案

  • cookie 大小不超过4kb
  • LocalStorage 大小在2.5M到10M之间(各家浏览器不同),而且只存储键值对,不提供搜索功能,不能建立自定义的索引。

MDN官网是这样解释indexedDB的

indexedDB是一种底层API,用于客户端存储大量的结构化数据,webStorage存储少量数据很有用,但是对于存储大量的结构化数据来说力不从心,而indexedDB很好的提供乐这种场景的解决方案。

通俗的说,indexedDB就是浏览器提供的本地数据库,它可以被网页脚本创建和操作。indexedDB允许存储大量的数据,提供查找的接口,还能建立索引,这些都是localstorage不具备的,就数据库类型而言,indexDB不属于关系型数据库(不支持SQL查询语句),更接近于NoSQL数据库。

indexedDB具有以下特点

  • 键值对存储
  • 异步
  • 支持事务(失败就会回滚)
  • 同源限制,每一个数据库对应创建它的域名
  • 存储空间大
  • 支持二进制存储

indexedDB只要用于哪些场景

  • 数据可视化等界面,大量数据,每次请求会消耗很大的性能
  • 即时聊天工具,大量信息需要存在本地
  • 其他存储方式容量不满足时

indexedDB重要概念

indexedDB是一个比较复杂的API,它把不同的实体,抽象成一个个对象接口。学习这个API,就是学习它的各种对象接口。

indexDB和WebSQL客户端离线存储大量数据_第8张图片

  • 数据库:IDBDatabase对象
    一系列相关数据的容器,每个域名都可以新建任意多个数据库。
    indexedDB数据库有版本的概念,同一个时刻,只能有一个版本的数据库存在,如果要修改数据库结构(新增或删除表,索引或者主键),只能通过升级数据库版本完成。

    var request=indexedDB.open(dbName,version) 打开数据库,没有则立马创建
    request.onsuccess=fucntion(event){
        db=event.target.result 获取到数据库对象
    }
    
  • 对象仓库:IDBObjectStore对象 表
    每个数据库包含若干个对象仓库(object store),它类似于关系型数据库的表格。

    request.onupgradeneeded=function(event){
        db=event.target.result 数据库对象
        var objectStore=db.createObjectStore() 创建仓库对象
        objectStore.createIndex() 创建索引
    }
    
    
  • 索引:IDBIndex对象
    加快查询速度,可以在仓库对象里,为不同的属性添加索引

  • 事务:IDBTransaction对象
    事务就是一连串的操作,要么都成功,失败了都回滚

    var request=db.transaction([storeName) 打开数据对象(表)的一个事务方法
                   .objectStore(storeName) 打开数据对象()
                   
    .add(数据库对象,数据对象(),data) 插入数据 /
    .get(数据库对象,数据对象(),data) 根据key查询 /
    .getAll(数据库对象,数据对象() 查询全部
      
    request.onsuccess=fucntion(){}
    request.onerror=function(){}
    
  • 操作请求:IDBRequest对象
    request 操作请求对象 用来监听事件 监听成功或失败或更新

    var request=indexedDB.open(dbName,version)   "创建或打开"的数据库对象
    
    var deleteRequest=window.indexedDB.deleteDatabase(dbName) "删除"的数据库对象
    * request.onsuccess
    * request.onerror
    * request.onupgradeneeded
    
  • 指针(游标):IDBCursor对象
    新的概念,比如我们要查询满足某一条件的所有数据时,就需要用到游标,游标一行一行的往下走,游标走到的地方就会返回这一行的数据,此时我们便可以对此数据进行判断,是否满足条件。直到数据为空停止。

  • 主键集合:IDBKeyRange对象

IndexDB实操

创建数据库和数据仓库


// js文件中封装一个函数用来创建数据库和数据仓库(表)
/**
 * @param{*} dbName 数据库名称
 * @param{*} version 数据库的版本
 * **/
function openDB(dbName,version=1){
    return new Promise((resolve,reject)=>{
        var db; 存储数据库对象
        // 打开数据库,如果没有就是创建操作
        var request=indexedDB.open(dbName,version)

        // 数据库打开或者创建成功
        request.onsuccess=function(event){
           // event.target.result 数据库对象
           db=event.target.result 
           console.log('数据库打开成功')
           resolve(db)
        }
        
        //打开或创建失败
        request.onerror=function(){
           console.log('数据库打开失败');
        }
        
        //数据库发生更新的时候
        //1.版本号更新 2.添加和删除表的时候
        //当第一次调用open方法时,会触发这个事件
        //初始化表
        request.onupgradeneeded=function(event){
            console.log('数据库版本更新');
            db=event.target.result
            // 创建数据仓库(表)
            var objectStore= db.createObjectStore("stu",{
                keyPath:"stuId",//这是主键
                autoIncrement:true //实现自增
            })  
            // 创建索引--> 查询更快
            objectStore.createIndex("stuId","stuId",{unique:true});
            objectStore.createIndex("stuName","stuName",{unique:false});
            objectStore.createIndex("stuAge","stuAge",{unique:false});
        }
    })
}

上述代码封装了一个openDB的函数,该函数调用了indexedDB.open的方法尝试打开一个数据库,如果数据库不存在,就会创建

indexedDB.open方法就会返回一个对象,这个对象上分别监听了成功,失败和更新这三个事件

// 调用封装的函数创建数据库和数据仓库

<body>
    <script src="./db.js">script>
    <script>
        openDB('stuDB',1.0)
    script>
body>

indexDB和WebSQL客户端离线存储大量数据_第9张图片

关闭数据库

// js文件中封装一个函数用来 关闭数据库
/**
 * 关闭数据库
 * @param {object} db  数据库实例
 * 
 * **/
function closeDB(db){
    db.close();
    console.log('数据库已关闭')
}

删除数据库

// js文件中封装一个函数用来 删除数据库
/**
 * 删除数据库
 * @param {object} dbName 数据库名称
 * 
 */
function deleteDBAll(dbName){
    console.log(dbName);
    let deleteRequest=window.indexedDB.deleteDatabase(dbName)
    deleteRequest.onerror=function(event){
        console.log('删除失败');
    }
    deleteRequest.onsuccess=function(event){
        console.log('删除成功');
    }
}

indexDB和WebSQL客户端离线存储大量数据_第10张图片
indexDB和WebSQL客户端离线存储大量数据_第11张图片

插入数据

// js文件中封装一个函数用来 插入数据
/**
 * 插入数据
 * @param {*} db 数据库实例
 * @param {*} storeName 数据仓库实例(表)
 * @param {*} data 要添加的数据
 *  
 */
function addData(db,storeName,data){
    //创建读写的事务
    //readwrite 打开模式:读写
    var request=db.transaction([storeName],'readwrite')
    //操作数据仓库(表)
     .objectStore(storeName)
     .add(data);
    request.onsuccess=function(){
        console.log('数据写入成功');
    },
    request.onerror=function(){
        console.log('数据写入失败');
    }
}   
//调用addData
<script>
 
//插入数据
//因为创建数据库和表的函数返回的是一个成功的promise,所以插入数据要写在后面的then方法中
openDB('stuDB','1.0')
    .then((db)=>{
        addData(db,'stu',{"stuId":1,"stuName":"张三","stuAge":18});
        addData(db,'stu',{"stuId":2,"stuName":"寒冰","stuAge":25});
        addData(db,'stu',{"stuId":3,"stuName":"德玛","stuAge":29});
        addData(db,'stu',{"stuId":4,"stuName":"张2","stuAge":18});
        addData(db,'stu',{"stuId":5,"stuName":"寒冰2","stuAge":25});
        addData(db,'stu',{"stuId":6,"stuName":"德玛2","stuAge":29});
        addData(db,'stu',{"stuId":7,"stuName":"张三","stuAge":18});
        addData(db,'stu',{"stuId":8,"stuName":"小黄","stuAge":25});
        addData(db,'stu',{"stuId":9,"stuName":"小王","stuAge":29});
        addData(db,'stu',{"stuId":10,"stuName":"小李","stuAge":18});
        addData(db,'stu',{"stuId":11,"stuName":"小吴","stuAge":25});
        addData(db,'stu',{"stuId":12,"stuName":"大苏打","stuAge":29});
        addData(db,'stu',{"stuId":13,"stuName":"牛人","stuAge":18});
        addData(db,'stu',{"stuId":14,"stuName":"大哥","stuAge":25});
        addData(db,'stu',{"stuId":15,"stuName":"二哥","stuAge":29});
        addData(db,'stu',{"stuId":16,"stuName":"三哥","stuAge":18});
        addData(db,'stu',{"stuId":17,"stuName":"四哥","stuAge":25});
        addData(db,'stu',{"stuId":18,"stuName":"张大头","stuAge":29});
        addData(db,'stu',{"stuId":19,"stuName":"李寿子","stuAge":18});
        addData(db,'stu',{"stuId":20,"stuName":"武器求","stuAge":25});
        addData(db,'stu',{"stuId":21,"stuName":"唐太宗","stuAge":29});
     } 
    )
       
</script>

indexDB和WebSQL客户端离线存储大量数据_第12张图片

读取数据

根据需求不同有不同的方式读取

1. 根据key查询

// js中封装一个根据key查询的方法
/**
 * 通过主键来读取数据
 * @param {any} db 数据库实例对象
 * @param {any} storeName 数据仓库(表) 实例对象
 * @param {any} key 主键
 *  
 */
function getDataByKey(db,storeName,key){
    return new Promise((resolve,reject)=>{
       var request= db.transaction([storeName])
          .objectStore(storeName)
          .get(key)
        request.onsuccess=function(){
            resolve(request.result)
        };

        request.onerror=function(){
            console.log('数据查询失败');
        }
    })
}

// 调用根据key查询的方法
  openDB('stuDB','1.0')
    .then((db)=>{
        //根据key查询
         return  getDataByKey(db,'stu',2) 
    }).then(stuInfo=>{
        console.log(stuInfo);
    })

indexDB和WebSQL客户端离线存储大量数据_第13张图片
2. 查询全部数据

// js中封装一个函数,用来查询所有的数据
/**
 * 查询所有数据
 * @param {any} db 数据库实例对象
 * @param {any} storeName 数据仓库(表) 实例对象
 
 *  
 */
function getAllData(db,storeName){
    return new Promise((resolve,reject)=>{
       var request= db.transaction([storeName])
          .objectStore(storeName)
          .getAll()
        request.onsuccess=function(){
            resolve(request.result)
        };

        request.onerror=function(){
            console.log('数据查询失败');
        }
    })
}

//调用查询全部数据的方法
openDB('stuDB','1.0')
.then((db)=>{
    //根据key查询
     return  getAllData(db,'stu',2) 
}).then(stuInfo=>{
    console.log(stuInfo);
})

indexDB和WebSQL客户端离线存储大量数据_第14张图片

3.通过指针进行查询


// js中封装一个函数,用来通过指针查询

/**
 * 通过游标(指针)来查询所有的数据
 * @param {*} db
 * @param {*} storeName
 
 */
function cursorGetData(db,storeName){
   return new Promise((resolve,reject)=>{
       var list=[] //用于存放所有的数据
       var request= db.transaction([storeName],"readwrite")
         .objectStore(storeName)
         .openCursor();//创建一个指针(默认指向第一条数据)
       
       request.onsuccess=function(event){
           var cursor=event.target.result;
           // 查看游标有没有返回一条数据
           if(cursor){ //指针对象
            console.log(cursor)
            list.push(cursor.value)
            cursor.continue(); //移动到下一条数据
           }else{
            resolve(list)
           }
       }
   })
}


// 调用指针查询的函数

 openDB('stuDB','1.0')
.then((db)=>{
  //根据cursor指针查询
  return cursorGetData(db,'stu')

}).then(stuInfo=>{
    console.log(stuInfo);
})
        

indexDB和WebSQL客户端离线存储大量数据_第15张图片

4.通过索引进行查询

只会返回满足条件的第一条数据

// js中封装一个方法,用来通过索引查询
/**
 * 根据索引来查询数据(只会返回满足条件的第一条数据)
 * @param {*} db
 * @param {*} storeName
 * @param {*} indexName 索引名称
 * @param {*} indexValue 索引值
  
 */
function getDataIndex(db,storeName,indexName,indexValue){
   return new Promise((resolve,reject)=>{
      //request 为操作请求对象
      var request=db.transaction([storeName],'readwrite')
      .objectStore(storeName)
      .index(indexName)
      .get(indexValue)

      request.onsuccess=function(event){
         resolve(event.target.result)
      }
      request.onerror=function(){
          console.log('索引查询失败');
      }
   })
}

// 调用索引查询的函数

 openDB('stuDB','1.0')
.then((db)=>{
  //根据索引来查询某一条数据
  //只是通过索引查询的化,只会返回满足条件的第一条数据
  return getDataIndex(db,'stu','stuAge',18)

}).then(stuInfo=>{
    console.log(stuInfo);
})

indexDB和WebSQL客户端离线存储大量数据_第16张图片

5.根据索引和游标来查询数据

  • IDBKeysRange对象代表数据库仓库(object store)里面的一组主键,根据这组主键,可以获取仓库或索引里面的一组记录

  • IDBkeyRange.lowerBound() 指定下限

// All keys ≥ x
var r1=IDBkeyRange.lowerBound(x)

// All keys > x
var r1=IDBkeyRange.lowerBound(x,true)
  • IDBKeyRange.upperBound() 指定上限
// All keys ≤ x
var r1=IDBkeyRange.upperBound(x)

// All keys < x
var r1=IDBkeyRange.upperBound(x,true)
  • IDBKeyRange.bound() 同事指定上下限
//  x ≤ All keys ≤ y
var r1= IDBKeyRange.bound(x,y) 

//  x < All keys < y
var r1= IDBKeyRange.bound(x,y,true,true) 

//  x < All keys  ≤  y
var r1= IDBKeyRange.bound(x,y,true,false) 

//  x ≤  All keys  < y
var r1= IDBKeyRange.bound(x,y,false,true) 

  • IDBKeyRange.only() 指定只包含一个值
// key=z
var r1 = IDBKeyRange.only(z)
// js中封装一个函数,根据索引和游标来查询数据
/**
 * 根据索引和游标来查询数据
 * @param {*} db
 * @param {*} storeName
 * @param {*} indexName 索引名称
 * @param {*} indexValue 索引值
  
 */
function getDataByIndex(db,storeName,indexName,indexValue){
    return new Promise((resolve,reject)=>{
        var list=[] //存储所有满足条件的数据
       //request 为操作请求对象
       var request=db.transaction([storeName],'readwrite')
       .objectStore(storeName)
       .index(indexName)
       .openCursor(IDBKeyRange.only(indexValue))//****
 
       request.onsuccess=function(event){
           var cursor =event.target.result
           console.log(cursor);
           if(cursor){
            list.push(cursor.value)
            cursor.continue()
           }else{
             resolve(list)
           }
       }
       request.onerror=function(){
           console.log('索引查询失败');
       }
    })
 }


// 调用索引和游标查询的函数

openDB('stuDB','1.0')
.then((db)=>{
  //根据索引和游标查询
  return getDataByIndex(db,'stu','stuAge',18)

}).then(stuInfo=>{
    console.log(stuInfo);
})

indexDB和WebSQL客户端离线存储大量数据_第17张图片
6.根据分页查询数据

indexedDB 分页查询不像MySql分页查询那么简单,没有提供现成的API,如limit等,所以需要我们自己实现分页

// js中封装一个函数,根据分页查询数据
/**
 * 根据索引和游标分页查询记录
 * @param {Object} db  数据库实例
 * @param {string } storeName 仓库名称
 * @param {string} indexName 索引名称
 * @param {string} indexValue 索引值
 * @param {number} page 页码
 * @param {number} pageSize 查询条数
 
 */
function cursorGetDataByIndexAndPage(
    db,
    storeName,
    indexName,
    indexValue,
    page,
    pageSize
){

   return new Promise((resolve,reject)=>{
      var list=[] //用于存储当前页的分页数据
      var counter=0 //创建一个计数器
      var isPass=true; //是否要跳过数据
      var  request=db.transaction([storeName],'readwrite')
                     .objectStore(storeName)
                     .openCursor();//创建一个游标对象(目前指向第一条数据)
      request.onsuccess=function(event){
           var cursor=event.target.result 
           // 判断是否要跳过一些数据
           if(page>1 && isPass){
              isPass=false
              cursor.advance((page-1)*pageSize) //跳过数据
              return;
           }

           if(cursor){
              list.push(cursor.value)
              counter++
              if(counter<pageSize){
                 cursor.continue()
              }else{
                 curser=null;
                 resolve(list)
              }
           }else{
             resolve(list)
           }

      }
   }) 
   
}

// 调用分页查询的函数
openDB('stuDB','1.0')
.then((db)=>{
   return cursorGetDataByIndexAndPage(
        db,
        'stu',
        '',
        '',
        4,
        5
    )

}).then(stuInfo=>{
    console.log(stuInfo);
})

indexDB和WebSQL客户端离线存储大量数据_第18张图片

7.更新

用put方法执行更改,如果没有就新增

默认跟着主键寻找,组件不能变

// 在js中封装一个方法,来更新数据
/**
 * 更新数据,如果没有就新增
 * @param {object} db
 * @param {string} storeName
 * @param {object} data
  
 */
function updateDB(db,storeName,data){
   return new Promise((resolve,reject)=>{
       var request=db.transaction([storeName],'readwrite')
       .objectStore(storeName)
       .put(data)
       request.onsuccess=function(){
          resolve({
            status:true,
            message:'更新数据成功'
          })
       }
   })
}
//调用方法来更新数据
openDB('stuDB','1.0')
.then((db)=>{

// 修改数据
// 主键不能变

return updateDB(db,'stu',{"stuId":13,"stuName":"浩哥无敌","stuAge":28})

}).then(stuInfo=>{
    console.log(stuInfo);
})

indexDB和WebSQL客户端离线存储大量数据_第19张图片
indexedDB中刷新数据,更新成功
indexDB和WebSQL客户端离线存储大量数据_第20张图片

8.删除

两种方式

  • 方式1:通过主键来删 删1条
//在js中封装一个方法 用于删除
/**
 * 删除数据 
 * @param {object} db
 * @param {string} storeName
 * @param {number} id
  
 */
function deleteDB(db,storeName,id){
    return new Promise((resolve,reject)=>{
        var request=db.transaction([storeName],'readwrite')
        .objectStore(storeName)
        .delete(id)
        request.onsuccess=function(){
           resolve({
             status:true,
             message:'删除数据成功'
           })
        }
    })
 }
 
// 调用删除的方法删除主键为4的数据
openDB('stuDB','1.0')
.then((db)=>{

 //删除数据
 //通过主键来删
 return deleteDB(db,'stu',4)

}).then(stuInfo=>{
    console.log(stuInfo);
})

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zPq4RNn9-1685670892706)(https://note.youdao.com/yws/res/25411/WEBRESOURCE1a0f084b7700d50e9ad8aa9457a4c790)]
indexDB和WebSQL客户端离线存储大量数据_第21张图片

  • 方式2 通过索引和游标删除指定的数据 删多条
// 封装一个方法通过索引和游标删除指定的数据
 /**
  * 通过索引和游标删除指定的数据
  * @param {object} db  数据库实例
  * @param {string} storeName 仓库名称
  * @param {string} indexName  索引名
  * @param {object} indexValue 索引值
  * 
  */
 function cursorDelete(db,storeName,indexName,indexValue){
    return new Promise((resolve,reject)=>{
        var request=db.transaction([storeName],'readwrite')
        .objectStore(storeName)
        .index(indexName) 
        .openCursor(IDBKeyRange.only(indexValue))
        request.onsuccess=function(event){
            var cursor=event.target.result
            if(cursor){
                var deleteRequest=cursor.delete()
                deleteRequest.onsuccess=function(){
                     resolve({
                        status:true,
                        message:'删除数据成功'
                     })
                }
            }
        }
    })
 }
// 调用通过索引和游标删除指定的数据的方法
// 调用删除的方法删除主键为4的数据
openDB('stuDB','1.0')
.then((db)=>{
//通过游标和索引删除 删除多条
return cursorDelete(db,'stu','stuAge',28)

}).then(stuInfo=>{
    console.log(stuInfo);
})

另外,由于indexedDB所提供的原生api比较复杂,所以现在出现了基于indexedDB封装的库,例如Dexie.js indexedDB的封装库Dexie

File System

File API 概述

以前,用户直接上传图片到服务器存储,然后服务器再将图片的地址返还回来,这样图片就能预览在页面上。这样做,会导致服务器有大量冗余的图片。而文件的大小,类型,验证都放在后端。

html有专门的上传文件的表单控件,这样用户体验会非常的差,客户端没法对用户选择的图片进行validate,无法获取文件的大小,无法预览和选择文件类型。如果是多文件传输,,javascript更是回天乏力。

现在,html提供了file API来帮助不通过服务器实现文件预览和图片预览,该接口允许JavaScript读取本地文件,但并不能直接访问本地文件,而是要依赖于用户行为,比如,用户在 type="file"控件上选择了某个文件或者用户将文件拖拽到浏览器上。

File Api 提供了以下几个接口来访问本地文件系统

  • File 单个文件,提供了诸如name,file size,minetype等只读文件属性
  • FileList 一个类数组File对象集合
  • FileReader:异步读取文件的接口
  • Blob 文件对象的二进制原始数据

File对象

File对象代表一个文件,用来读写文件信息。它继承了Blob对象,或者说是一种特殊的Blob对象,所有可以使用Blob对象的场合都可以使用它。

最常见的使用场合是表单的文件,用户选中文件之后,浏览器会生成一个数组,里面是每一个用户选中的文件,他们都是file实例对象。

<body>
    <input type="file" name="" id="file" multiple>
    <script>
        var file=document.getElementById('file') //拿到控件
        file.onchange=function(event){ //绑定change事件
             console.log(event.target.files)//fileList,包含多个file对象  
        }
    </script>
</body>

indexDB和WebSQL客户端离线存储大量数据_第22张图片

构造函数

浏览器原生提供了一个File()构造函数,用来构建File实例对象
indexDB和WebSQL客户端离线存储大量数据_第23张图片

new File(array,name,[{options}])

File()构造函数接收三个参数

  • Array 一个数组,成员可以是二进制或字符串,表示文件的内容
  • name 字符串,表明文件名或文件路径
  • options 配置对象,设置实例的属性,【可选】
    • 属性:type 字符串,表示实例对象的MIME类型,默认值为空字符串
    • 属性:lastModified 时间戳,表示上次修改的时间,默认为Date.now()
var file=new File(
    ['foo'],
    'foo.txt',
    {
       type: 'text/plain'
    }
)
console.log('创建时间',file.lastModified)
console.log('创建文件文字',file.name)
console.log('创建文件的大小',file.size)
console.log('创建的文件类型',file.type)

FileList对象

  • FileList是一个类数组,每一个成员都是一个file实例
  • 文件控件通过change事件的event.target.files返回的就是一个FileList实例
  • 获取FileList类数组中第几个文件的方式

    
    


FileReader对象

indexDB和WebSQL客户端离线存储大量数据_第24张图片

  • 用来读取File对象或Blob对象所包含的文件内容
  • 浏览器提供了FileReader构造函数,用来生成FileReader实例
var readerFile=new FileReader()

FileReader有以下的实例属性

  • FileReader.onload load事件,读取操作完成的监听函数,通常在这个函数里面使用result属性,拿到文件内容。 ++event.target.result++

  • FileReader.error 读取文件时产生的错误对象

  • FileReader.readyState 表示读取文件时当前的状态,0 尚未加载,1数据正在加载,2表示加载完成

  • FileReader.result 读取完成后的文件内容,有可能是字符串,有可能是一个ArrayBuffer实例。

  • FileReader.onabort abort事件,用户终止读取操作的监听函数

  • FileReader.onloadstart loadstart事件,读取操作开始的监听函数

  • FileReader.onloadend loadend事件,读取操作结束的监听函数

  • FileReader.onprogress progress事件,读取操作进行中的监听函数

FileReader有以下的实例方法

  • FileReader.readAsText() 读取文件的内容,读取完成后,result属性将返回文件内容的文本字符串,参数1是代表文件的Blob实例,第二个参数可选项,表示文本编码,默认为UTF-8.

  • FileReader.readAsDataURL() 读取图片,读取完成后,result属性返回一个Data URL格式(Base64编码)的字符串,代表图片内容。可直接用于img元素的src标签,如果需要base64解码,必须要把前缀data:***;base63从字符串中删除以后,再进行解码。

  • FileReader.abort() 终止读取操作,readyState属性将变成2

  • FileReader.readAsArrayBuffer() 以ArrayBuffer的格式读取文件,读取完成后result属性将返回一个ArrayBuffer实例

  • FileReader.readAsBinaryString() 读取完后,result属性将返回原始的二进制数据

// 小案例:读取文本文件和读取图片
<body>
    <input type="file" name="" id="file">
    <img src="" alt="" width="200">
    <script>
        var file=document.getElementById('file')
        file.onchange=function(event){
            var fileHandle=event.target.files[0]
            console.log(fileHandle);
            // 异步读取文本文件 readAsText
            // var reader=new FileReader()
            // reader.readAsText(fileHandle)  
            // reader.οnlοad=function(event){
            //     console.log(event.target.result)
            // }


            //异步读取图片直接在页面预览 readAsDateUrl
            var reader=new FileReader()
            reader.readAsDataURL(fileHandle)
            reader.onload=function(event){
                 let img=document.querySelector('img')
                 img.src=event.target.result
            }
        }
    </script>
</body>


File System Access API(了解)

  • File System Access API和File API是两套规范,

  • File System Access API提供了比较稳妥的本地文件交互模式,即保证了实用价值,又保障了用户的数据安全。从打开、到编辑、到保存、一套到底

  • 但是,目前,只有chrome谷歌支持该API.

你可能感兴趣的:(交互,javascript,前端,vue.js,ecmascript,前端框架)