现如今,随着浏览器功能的日益强大,为了节省网络资源和增加用户体验,越来越多的数据被存储在客户端。常见的浏览器离线存储数据的方式有Cookie、webStorage、webSQL、indexDB、File system等,常用的cookie只能存储4kb的数据,而localStorage,sessionStorage只能存储2.5M-10M的数据,并且在代码中是同步执行的。
那么如果做可视化大屏或者聊天室这类应用,我们想在客户端存储大量数据,应该怎么做呢?
可以用到的技术有 WebSQL(官方已不再维护,现代浏览仍然保留其功能,仅作了解) 和 indexDB(支持存储大于250M的数据,并且是异步执行的。)
WebSQL数据库API并不是HTML5规范的一部分。但是它是一个独立的规范,引入了一组使用SQL操作客户端数据库的APIS
最新版的safari,chrome和Opera浏览器都支持WebSQL
目前,webSQL已经不再是W3C推荐的规范,官方已经不再维护,因为当前的SQL规范采用SQLite的SQL方言,而作为一个标准,这是不可接收的
webSQL使用SQL语言来进行操作,更像是一个关系型数据库,而indexDB则更像是一个非关系型数据库,这也是目前W3c强推的浏览器数据库解决方案。
在WebSQL中,有3个核心方法
用openDatebase()方法来打开已经存在的数据库,如果数据库不存在,则会创建一个新得数据库,使用代码如下:
openDatabase()方法对应5个参数
<body>
<script>
var db=openDatabase('mydb','1.0','Test DB',2*1024*1024)
script>
body>
上面的代码表示,打开一个名为mydb的数据库,如果不存在,则会创建该数据库,版本号为1.0,大小为2M.
使用代码database.transaction()创建表格和数据
database.transaction(function(tx){
tx.executeSql()
})
executeSql(sqlStatement,arguments,callback,errorCallback)
该方法有4个参数
静态插入数据:
<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>
动态插入数据:
<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)
})
// 删除数据
var stuId=1
db.transaction(function(tx){
tx.executeSql('DELETE FROM STU WHERE stuId=?',[stuId])
})
要修改数据也是使用SQL中的语法,同样也支持动态指定的方式
var stuName="寒冰"
db.transaction(function(tx){
tx.executeSql('UPDATE STU SET stuName=? WHERE stuId=2',[stuName])
})
随着浏览器的功能不断增强,越来越多的网站开始考虑,将大量的数据存储在客户端,这样可以减少服务端获取数据,直接从本地获取数据。
现有的浏览器数据存储方案
MDN官网是这样解释indexedDB的
indexedDB是一种底层API,用于客户端存储大量的结构化数据,webStorage存储少量数据很有用,但是对于存储大量的结构化数据来说力不从心,而indexedDB很好的提供乐这种场景的解决方案。
通俗的说,indexedDB就是浏览器提供的本地数据库,它可以被网页脚本创建和操作。indexedDB允许存储大量的数据,提供查找的接口,还能建立索引,这些都是localstorage不具备的,就数据库类型而言,indexDB不属于关系型数据库(不支持SQL查询语句),更接近于NoSQL数据库。
indexedDB具有以下特点
indexedDB只要用于哪些场景
indexedDB重要概念
indexedDB是一个比较复杂的API,它把不同的实体,抽象成一个个对象接口。学习这个API,就是学习它的各种对象接口。
数据库: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对象
// 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>
// 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('删除成功');
}
}
// 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>
根据需求不同有不同的方式读取
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);
})
// 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);
})
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);
})
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);
})
5.根据索引和游标来查询数据
IDBKeysRange对象代表数据库仓库(object store)里面的一组主键,根据这组主键,可以获取仓库或索引里面的一组记录
IDBkeyRange.lowerBound() 指定下限
// All keys ≥ x
var r1=IDBkeyRange.lowerBound(x)
// All keys > x
var r1=IDBkeyRange.lowerBound(x,true)
// All keys ≤ x
var r1=IDBkeyRange.upperBound(x)
// All keys < x
var r1=IDBkeyRange.upperBound(x,true)
// 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)
// 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);
})
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);
})
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);
})
8.删除
两种方式
//在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);
})
// 封装一个方法通过索引和游标删除指定的数据
/**
* 通过索引和游标删除指定的数据
* @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
以前,用户直接上传图片到服务器存储,然后服务器再将图片的地址返还回来,这样图片就能预览在页面上。这样做,会导致服务器有大量冗余的图片。而文件的大小,类型,验证都放在后端。
html有专门的上传文件的表单控件,这样用户体验会非常的差,客户端没法对用户选择的图片进行validate,无法获取文件的大小,无法预览和选择文件类型。如果是多文件传输,
,javascript更是回天乏力。
现在,html提供了file API来帮助不通过服务器实现文件预览和图片预览,该接口允许JavaScript读取本地文件,但并不能直接访问本地文件,而是要依赖于用户行为,比如,用户在 type="file"控件上选择了某个文件或者用户将文件拖拽到浏览器上。
File Api 提供了以下几个接口来访问本地文件系统
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>
浏览器原生提供了一个File()构造函数,用来构建File实例对象
new File(array,name,[{options}])
File()构造函数接收三个参数
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)
通过change事件的event.target.files
返回的就是一个FileList实例
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 API是两套规范,
File System Access API提供了比较稳妥的本地文件交互模式,即保证了实用价值,又保障了用户的数据安全。从打开、到编辑、到保存、一套到底
但是,目前,只有chrome谷歌支持该API.