10-2-同源策略
第十章 BOM¶
统计信息:字数 29680 阅读60分钟
2021-11-24 2023-04-11
10.7 同源策略¶
Same-origin 介绍¶
起源:不同源的网页不能打开cookie。
同源:协议域名端口都相同。
目的:保证用户数据的安全,防止恶意网站窃取用户数据。
现在:无法获取非同源网页的cookie,localStorage,IndexedDB;DOM;无法发送AJAX请求。
非同源网页可以调用下面的属性和方法。
window.closed
window.frames
window.length
window.location
window.opener
window.parent
window.self
window.top
window.window
window.blur()
window.close()
window.focus()
window.postMessage()
Cookie 共享¶
两个网页的一级域名相同(xxx.com)次级域名不同(docs.demo)可以设置 document.domain 相同来共享cookie,设置是会重置端口为null。两个页面需要同时设置 document.domain = 'xxx.com'; 才能达到同源的目的。设置后两个界面可以通过 document.cookie 获取相同的信息。
iframe多窗口通信¶
一个界面中嵌入多个窗口,一个窗口可以获取父窗口和子窗口的window,如果不同源,不能操作DOM(document.getElementByID('myIframe')..contentWindow.document 会报错)。
如果两个窗口的一级域名相同,次级域名不同,可以类似cookie 设置 document.domain。
如果l两个窗口完全不同源,需要使用下面的两个方案(开发中未使用):
片段识别符
片段标识符(fragment identifier)指的是,URL 的#
号后面的部分,比如http://example.com/x.html#fragment
的#fragment
。如果只是改变片段标识符,页面不会重新刷新。
父窗口可以把信息,写入子窗口的片段标识符。
var src = originURL + '#' + data;
document.getElementById('myIFrame').src = src;
上面代码中,父窗口把所要传递的信息,写入 iframe 窗口的片段标识符。
子窗口通过监听hashchange
事件得到通知。
window.onhashchange = checkMessage;
function checkMessage() {
var message = window.location.hash;
// ...
}
同样的,子窗口也可以改变父窗口的片段标识符。
parent.location.href = target + '#' + hash;
window.postMessage()
上面的这种方法属于破解,HTML5 为了解决这个问题,引入了一个全新的API:跨文档通信 API(Cross-document messaging)。
这个 API 为window
对象新增了一个window.postMessage
方法,允许跨窗口通信,不论这两个窗口是否同源。举例来说,父窗口aaa.com
向子窗口bbb.com
发消息,调用postMessage
方法就可以了。
// 父窗口打开一个子窗口
var popup = window.open('http://bbb.com', 'title');
// 父窗口向子窗口发消息
popup.postMessage('Hello World!', 'http://bbb.com');
postMessage
方法的第一个参数是具体的信息内容,第二个参数是接收消息的窗口的源(origin),即“协议 + 域名 + 端口”。也可以设为*
,表示不限制域名,向所有窗口发送。
子窗口向父窗口发送消息的写法类似。
// 子窗口向父窗口发消息
window.opener.postMessage('Nice to see you', 'http://aaa.com');
父窗口和子窗口都可以通过message
事件,监听对方的消息。
// 父窗口和子窗口都可以用下面的代码,
// 监听 message 消息
window.addEventListener('message', function (e) {
console.log(e.data);
},false);
message
事件的参数是事件对象event
,提供以下三个属性。
event.source
:发送消息的窗口event.origin
: 消息发向的网址event.data
: 消息内容
下面的例子是,子窗口通过event.source
属性引用父窗口,然后发送消息。
window.addEventListener('message', receiveMessage);
function receiveMessage(event) {
event.source.postMessage('Nice to see you!', '*');
}
上面代码有几个地方需要注意。首先,receiveMessage
函数里面没有过滤信息的来源,任意网址发来的信息都会被处理。其次,postMessage
方法中指定的目标窗口的网址是一个星号,表示该信息可以向任意网址发送。通常来说,这两种做法是不推荐的,因为不够安全,可能会被恶意利用。
event.origin
属性可以过滤不是发给本窗口的消息。
window.addEventListener('message', receiveMessage);
function receiveMessage(event) {
if (event.origin !== 'http://aaa.com') return;
if (event.data === 'Hello World') {
event.source.postMessage('Hello', event.origin);
} else {
console.log(event.data);
}
}
LocalStorage
通过window.postMessage
,读写其他窗口的 LocalStorage 也成为了可能。
下面是一个例子,主窗口写入 iframe 子窗口的localStorage
。
window.onmessage = function(e) {
if (e.origin !== 'http://bbb.com') {
return;
}
var payload = JSON.parse(e.data);
localStorage.setItem(payload.key, JSON.stringify(payload.data));
};
上面代码中,子窗口将父窗口发来的消息,写入自己的 LocalStorage。
父窗口发送消息的代码如下。
var win = document.getElementsByTagName('iframe')[0].contentWindow;
var obj = { name: 'Jack' };
win.postMessage(
JSON.stringify({key: 'storage', data: obj}),
'http://bbb.com'
);
加强版的子窗口接收消息的代码如下。
window.onmessage = function(e) {
if (e.origin !== 'http://bbb.com') return;
var payload = JSON.parse(e.data);
switch (payload.method) {
case 'set':
localStorage.setItem(payload.key, JSON.stringify(payload.data));
break;
case 'get':
var parent = window.parent;
var data = localStorage.getItem(payload.key);
parent.postMessage(data, 'http://aaa.com');
break;
case 'remove':
localStorage.removeItem(payload.key);
break;
}
};
加强版的父窗口发送消息代码如下。
var win = document.getElementsByTagName('iframe')[0].contentWindow;
var obj = { name: 'Jack' };
// 存入对象
win.postMessage(
JSON.stringify({key: 'storage', method: 'set', data: obj}),
'http://bbb.com'
);
// 读取对象
win.postMessage(
JSON.stringify({key: 'storage', method: "get"}),
"*"
);
window.onmessage = function(e) {
if (e.origin != 'http://aaa.com') return;
console.log(JSON.parse(e.data).name);
};
Ajax 处理跨域¶
服务器端处理:架设代理服务器:浏览器访问同源服务器,服务器请求外部服务。还有三种方法:JSONP,websocket, CORS。
1、JSONP¶
JSONP 简单易用,没有兼容性问题,老式浏览器全部支持,服务端改造非常小。
它的做法如下。
第一步,网页添加一个<script>
元素,向服务器请求一个脚本,这不受同源政策限制,可以跨域请求。
<script src="http://api.foo.com?callback=bar"></script>
注意,请求的脚本网址有一个callback
参数(?callback=bar
),用来告诉服务器,客户端的回调函数名称(bar
)。
第二步,服务器收到请求后,拼接一个字符串,将 JSON 数据放在函数名里面,作为字符串返回(bar({...})
)。
第三步,客户端会将服务器返回的字符串,作为代码解析,因为浏览器认为,这是<script>
标签请求的脚本内容。这时,客户端只要定义了bar()
函数,就能在该函数体内,拿到服务器返回的 JSON 数据。
下面看一个实例。首先,网页动态插入<script>
元素,由它向跨域网址发出请求。
function addScriptTag(src) {
var script = document.createElement('script');
script.setAttribute('type', 'text/javascript');
script.src = src;
document.body.appendChild(script);
}
window.onload = function () {
addScriptTag('http://example.com/ip?callback=foo');
}
function foo(data) {
console.log('Your public IP address is: ' + data.ip);
};
上面代码通过动态添加<script>
元素,向服务器example.com
发出请求。注意,该请求的查询字符串有一个callback
参数,用来指定回调函数的名字,这对于 JSONP 是必需的。
服务器收到这个请求以后,会将数据放在回调函数的参数位置返回。
foo({
'ip': '8.8.8.8'
});
由于<script>
元素请求的脚本,直接作为代码运行。这时,只要浏览器定义了foo
函数,该函数就会立即调用。作为参数的 JSON 数据被视为 JavaScript 对象,而不是字符串,因此避免了使用JSON.parse
的步骤。
2、WebSocket¶
WebSocket 是一种通信协议,使用ws://
(非加密)和wss://
(加密)作为协议前缀。该协议不实行同源政策,只要服务器支持,就可以通过它进行跨源通信。
下面是一个例子,浏览器发出的 WebSocket 请求的头信息。
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com
上面代码中,有一个字段是Origin
,表示该请求的请求源(origin),即发自哪个域名。
正是因为有了Origin
这个字段,所以 WebSocket 才没有实行同源政策。因为服务器可以根据这个字段,判断是否许可本次通信。如果该域名在白名单内,服务器就会做出如下回应。
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat
3、CORS¶
10.8 CORS¶
CORS概述¶
CORS 是跨源资源分享(Cross-Origin Resource Sharing)的缩写,它是 W3C 标准,属于跨源 AJAX 请求的根本解决方法。相比 JSONP 只能发GET
请求,CORS 允许任何类型的请求。JSONP 的优势在于支持老式浏览器,以及可以向不支持 CORS 的网站请求数据。
兼容:CORS 需要浏览器和服务器同时支持,目前浏览器都支持。对于前端开发,CORS和AJAX差不多,如果发出的请求是跨域的,那么在请求头更改信息,用户体验良好。后端开发需要在服务器实现CORS API。
分类:简单请求和非简单请求
简单请求:请求方法(get-post-head)和请求头信息简单,请求头只能是下面的属性。简单请求类似于表单请求,浏览器允许表单请求跨域。
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
简单请求¶
浏览器发现请求跨域,需要在请求头中加入 Origin: https://demo.xxx.com 字段,包括当前请求的协议域名端口号;服务器判断这个origin在许可的范围内,在相应头中添加 Access-Control-Allow-Origin 字段,实现跨域;如果这个origin不在许可范围内,就返回正常的HTTP回应,不包含 Access-Control-Allow-Origin 字段,此时前端xhr.onerror 会捕获到这个错误。下面是正确的字符说明
// 服务器的响应头
Access-Control-Allow-Origin: http://api.bob.com // 必选参数,返回值是origin的参数,或者是*,表示接受任意域名的请求
Access-Control-Allow-Credentials: true // 可选参数:这个值表示是否发送cookie,只能设置为true,不设置就表示false(默认浏览器cookie不包括在CORS请求中)
Access-Control-Expose-Headers: FooBar // 可选参数:JS脚本默认可以获取部分响应头信息,这里加上这个属性,表示可以获取这个信息:getResponseHeader('FooBar')
Content-Type: text/html; charset=utf-8
默认CORS请求不包含 cookie,为了避免CSRF风险。如果需要发送cookie,前端需要设置 xhr.withCredentials = true; 服务器会返回 Access-Control-Allow-Credentials: true,实现跨域。
如果服务器要求浏览器发送 Cookie,Access-Control-Allow-Origin
就不能设为星号,必须指定明确的、与请求网页一致的域名。
非简单请求¶
非简单请求:请求的方法特殊(PUT DELETE)请求头的字段特殊(Content-Type: 'application/json')
第一步 预检请求(preflight)
在通信开始前,发送一个预检请求,浏览器询问服务器当前网页所在域名是否在服务器的许可列表中,哪些请求方法和请求头字段是允许的。服务器响应同意后,浏览器可以继续发送请求。如果不同意就不能发送;这样避免了服务器收到大量跨域的PUT delete请求。下面是一个预检请求的请求头:
OPTIONS /cors HTTP/1.1 预检请求的方法是 OPTIONS
Origin: http://api.bob.com 源是网页的网址
Access-Control-Request-Method: PUT 这是可以接受的请求方法
Access-Control-Request-Headers: X-Custom-Header 这里是自定义的请求头字段
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
第二步 服务器响应
服务器收到预检请求,允许跨域,做出回应,响应头如下
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://api.bob.com 这里表示当前网页可以跨域
Access-Control-Allow-Methods: GET, POST, PUT 这里是可以跨域的方法
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain
如果不允许跨域,onerror 会捕获错误
XMLHttpRequest cannot load http://api.alice.com.
Origin http://api.bob.com is not allowed by Access-Control-Allow-Origin.
第三步 浏览器正常的请求
浏览器自动添加Origin字段,发送正常的请求
10.9 Storage¶
Storage API 的实例对象有两个:window.localStorage and window.sessionStorage。他们的区别是,关闭当前窗口(对话)后存储是否继续保留。保存的数据以键值对保存,实际上是一个字符串(数值参数会转化成字符串存储)。每个域名的存储不同,最小的chrome是2.5MB,也受同域限制,跨域操作会报错。
属性和方法
1、window.localStorage.length
2、window.localStorage.setItem('key', 'value'); // 这里设置的key-value都是字符串,其他数据类型会强制转换成字符串,方法没有返回值。如果存储满了,这个方法会报错。或者直接改变属性也可以。如果已有这个key, 重新设置会覆盖原始值。
window.localStorage['key'] = value;
3、window.sessionStorage.getItem('foo') 如果不存在就返回 null
4、window.sessionStorage.removeItem('foo') 如果key不存在,方法不会报错;
5、window.sessionStorage.clear(); // return undefined
6、window.sessionStorage.key 返回一个key的伪数组,结合length可以遍历;
存储事件:当存储内容变化时,会触发storage事件。这个事件很特殊:如果一个窗口存储变化,那么当前窗口不会触发,同一个域名的其他窗口会触发这个事件(可以使用这个属性进行不同窗口的通信)
window.addEventListener('storage', onChange);
onChange = (e) => {
console.log(e.key); 表示变动的键名(如果是clear事件返回null)
e.newValue
e.oldValue
e.url
e.storageArea
}
localStorage 的可以参考自己总结的博客。
https://blog.csdn.net/weixin_41697143/article/details/98479748
10.9 History¶
浏览器的历史对象。JS脚本不允许获取历史地址,但是可以跳转导航。对历史对象的操作,和浏览器的前进后退按钮相同。
属性:history.length 当前历史的条数;history.state 历史堆栈顶层的属性(默认undefined)
方法:
- history.back(); history.forward(); 这里会从缓存中加载页面。
- history.go(0) 默认是0,表示刷新界面;使用加减1表示网页前进后退。
- History.pushState(state, title, url) state 表示状态对象,当popstate事件触发后,这个对象会传入回调函数。title 表示新页面标题,url 表示新的网址。这个方法会把当前的地址改成URL,history中会增加一个历史信息,但是界面不会跳转(不会刷新)这个网址不能跨域,否则报错。
- History.replaceState(state, title, url) 替换当前的历史记录为新的URL(原始历史被替换)
事件:popstate
当history对象变化时会触发这个事件:pushState() and replaceState() 不会触发这个事件,其他三个事件或者点击前进后退按钮就会触发这个事件。可以给这个事件指定回调函数。注意,页面第一次加载的时候,浏览器不会触发popstate
事件。
10.10 Location, URL, URLSearchParems 对象¶
Location¶
Location 对象可以操作 URL,使用 window.location or document.location 可以获取Location对象。下面是常用的属性:
location.href 整个URL (如果更改,浏览器会立刻跳转到新地址,即使URL与原始相同,界面仍然会刷新)
location.origin 协议、主机名、端口 http://www.baidu.com:8080(这是只读属性)
location.protocol 协议(包括冒号) http:
location.host 主机名和端口号 www.baidu.com:8080(默认80and443端口会省略)
hostname 主机名
port 端口号
pathname 路径:从根路径开始(包括根路径)
search 查询部分:问号开始(不包括问号)
hash 哈希部分:井号开始(不包括#)
username
password 域名前面的用户名和密码 测试是undefined
更改 location.href 可以变成新的锚点('#comment')。Location.href
属性是浏览器唯一允许跨域写入的属性,即非同源的窗口可以改写另一个窗口(比如子窗口与父窗口)的Location.href
属性,导致后者的网址跳转。Location
的其他属性都不允许跨域写入。
方法:
- location.assign(newURL) 浏览器立刻跳转到新的URL,如果不是URL会报错
- location.replace(newURL) 直接跳转到新的网页,当前的历史不会保存(可以判断是否是移动端,然后界面进行跳转);
- location.reload(true) 重载当前窗口(刷新界面)如果传入true,表示想服务器重新请求,请求新界面不滚动;如果是false或者默认不传值,直接从缓存中加载,界面停留在当前滚动的位置。
- location.toString() 等价于 let a = location.href;
URL¶
1、URL的编码和解码¶
URL中的汉字和特殊字符需要转码成为UTF-8编码(例如%82)请求中路径文件名需要转码
- encodeURI('http://www.example.com/q=春节') (注意是URI,不是URL)传入一个字符串,会将元字符(冒号斜杠)和语义字符之外的字符,都进行转义,这里可以传入一个完整的URL
- encodeURIComponent(filepath + name)会转码除了语义字符之外的所有字符,即元字符也会被转码。如果斜杠和冒号被转义就会出错。
- decodeURI
- decodeURIComponent 是上面两个方法的逆运算
2、URL对象¶
URL对象是一个原生对象,a标签继承了URL对象的属性和方法(href)
var url = new URL('http://www.example.com/index.html');
console.log(url.href);
如果参数是另一个 URL 实例,构造函数会自动读取该实例的href属性,作为实际参数。如果 URL 字符串是一个相对路径,那么需要表示绝对路径的第二个参数,作为计算基准。
var url2 = new URL('page2.html', 'http://example.com/page1.html');
// "http://example.com/page2.html"
属性:URL实例对象的属性和Location的属性基本一致(origin只读,其他都可以修改)
方法
- URL.createObjectURL()为上传/下载的文件、流媒体文件生成一个 URL 字符串。这个字符串代表了
File
对象或Blob
对象的 URL。 - URL.revokeObjectURL()用来释放
URL.createObjectURL
方法生成的 URL 实例。它的参数就是URL.createObjectURL
方法返回的 URL 字符串。
3、URLSearchParams¶
URLSearchParams 用来构造、解析、处理 URL 查询部分(?后面的部分)参数可以使字符串对象数组,会对查询字符串自动编码(encode)。
// 方法一:传入字符串
var params = new URLSearchParams('?foo=1&bar=2');
// 方法二:传入数组
var params = new URLSearchParams([['foo', 1], ['bar', 2]]);
// 方法三:传入对象
var params = new URLSearchParams({'foo' : 1 , 'bar' : 2});
浏览器向服务器发送表单数据时,可以直接使用URLSearchParams
实例作为表单数据(POS请求的数据)。
fetch('https://example.com/api', {
method: 'POST',
body: params
}).then(...)
var params = new URLSearchParams({version: 2.0});
window.location.href = location.pathname + '?' + params;
- URLSearchParams.toString() 把实例对象转化成字符串
- URLSearchParams.append('baz', 3) 追加一个查询参数。第一个为键名,第二个为键值,没有返回值。
- URLSearchParams.delete() 删除一个查询参数。
- URLSearchParams.has() 查询一个参数 返回布尔值
- URLSearchParams.set('foo', 5) 设置一个查询参数;如果没有这个参数就增加
- URLSearchParams.get() 获取某个value
- URLSearchParams.getAll() 返回全部的value数组
- URLSearchParams.sort() 按照Unicode进行排序
- URLSearchParams.keys()、URLSearchParams.values()、URLSearchParams.entries() 返回一个遍历器对象,让for...of... 循环遍历。
keys
方法返回的是键名的遍历器,values
方法返回的是键值的遍历器,entries
返回的是键值对的遍历器。
10.11 ArrayBuffer, Blob¶
ArrayBuffer 对象¶
可以获取操作二进制数据(操作内存),主要在ES6中介绍。
let buffer = new ArrayBuffer(8); // 构造函数创建实例对象,表示这段二进制数据占用多少个字节
buffer.byteLength; 当前对象占用的内存长度
let buffer2 = buffer.slice(0,4); 复制一部分内存
Blob 对象¶
表示二进制文件的数据内容(图片)Binary Large Object 二进制大型对象,可以用来操作二进制文件
构造函数
let blob = newBlob([array], options);
// 第一个参数是二进制数组,数组成员是二进制数据或者字符串,第二个可选参数是配置,type,表示数据类型
let htmlFragment = ['<span>Michael</span>'];
let blob = new Blob(htmlFragemtn, {
type: 'text/html'
});
let obj = { foo: 'foo' };
let blob2 = new Blob([JSON.stringify(obj)], {
type: 'application/json'
});
属性和方法
size 返回二进制文件的长度
type 返回二进制文件的类型
blob.slice(start, end, type) 截取一部分二进制文件(可以设置类型)
下面使用Blob读取上传的文件
获取文件¶
input type=file 的上传组件,浏览器不允许脚本直接控制value,必须用户上传。用户选好文件后,即可读取文件。文件输入input和拖拽对象dataTransfer 返回的是一个类数组,FileList,每一个item是一个File对象(File对象是Blob构造函数的特殊实例,具有 name,lastModifiedData 的属性)
// HTML 代码如下
// <input type="file" accept="image/*" multiple onchange="fileinfo(this.files)"/>
function fileinfo(files) {
for (let i = 0; i < files.length; i++) {
console.log(file);
}
}
下载文件¶
在AJAX请求中,如果设置responseType = blob 下载的结果就是Blob对象
let xhr = new XMLHttpRequest();
xhr.open('GET', null);
xhr.responseType = 'blob';
xhr.onload = () => {
console.log(xhr.response);
}
xhr.send(null);
生成URL¶
浏览器允许使用URL.createObjectURL()
方法,针对 Blob 对象生成一个临时 URL,以便于某些 API 使用。
var droptarget = document.getElementById('droptarget');
droptarget.ondrop = (e) => {
let files = e.dataTransfer.files;
for (let i = 0; i < files.length; i++) {
let type = files[i].type;
if (type.substring(0, 6) !== 'images/') {
continue;
}
let img = document.createElement('img');
img.src = URL.createObjectURL(files[i]);
img.onload = () => {
this.width = 100;
document.body.appendChild(this);
URL.revokeObjectURL(this.src);
}
}
}
读取文件¶
取得 Blob 对象以后,可以通过FileReader
对象,读取 Blob 对象的内容,即文件内容。
FileReader 对象提供四个方法,处理 Blob 对象。Blob 对象作为参数传入这些方法,然后以指定的格式返回。
FileReader.readAsText()
:返回文本,需要指定文本编码,默认为 UTF-8。FileReader.readAsArrayBuffer()
:返回 ArrayBuffer 对象。FileReader.readAsDataURL()
:返回 Data URL。FileReader.readAsBinaryString()
:返回原始的二进制字符串。
下面是FileReader.readAsText()
方法的例子,用来读取文本文件。
// HTML 代码如下
// <input type=’file' onchange='readfile(this.files[0])'></input>
// <pre id='output'></pre>
function readfile(file) {
var reader = new FileReader();
reader.readAsText(file);
reader.onload = function () {
var text = reader.result;
var out = document.getElementById('output');
out.innerHTML = '';
out.appendChild(document.createTextNode(text));
}
reader.onerror = function(e) {
console.log('Error', e);
};
}
上面代码中,通过指定 FileReader 实例对象的onload
监听函数,在实例的result
属性上拿到文件内容。
下面是FileReader.readAsArrayBuffer()
方法的例子,用于读取二进制文件。
// HTML 代码如下
// <input type="file" onchange="typefile(this.files[0])"></input>
function typefile(file) {
// 文件开头的四个字节,生成一个 Blob 对象
var slice = file.slice(0, 4);
var reader = new FileReader();
// 读取这四个字节
reader.readAsArrayBuffer(slice);
reader.onload = function (e) {
var buffer = reader.result;
// 将这四个字节的内容,视作一个32位整数
var view = new DataView(buffer);
var magic = view.getUint32(0, false);
// 根据文件的前四个字节,判断它的类型
switch(magic) {
case 0x89504E47: file.verified_type = 'image/png'; break;
case 0x47494638: file.verified_type = 'image/gif'; break;
case 0x25504446: file.verified_type = 'application/pdf'; break;
case 0x504b0304: file.verified_type = 'application/zip'; break;
}
console.log(file.name, file.verified_type);
};
}
10.12 File, FileList, FileReader¶
File 对象:File对象是一个特殊的Blob实例,文件上传后,即可从input元素中拿到这个对象
let file = document.getElementById('file-update').files[0];
构造函数
let file = new File(array, name, options);
array 是文件数组(可以使arraybuffer或者string,name是文件路径或者文件名,options是配置文件,type="application/json", lastModified: 时间戳)
属性和方法
file.name
file.size
file.type
file.lastModified
file.slice() 这是继承自Blob对象的方法
FileList 伪数组,每一项是一个File对象:文件输入选择或者拖拽元素节点返回的就是FileList对象。FileList.length 返回的长度表示包含文件的个数。
FileReader 对象用于读取文件对象的内容。
let reader = new FileReader();
属性和方法
reader.error
reader.readyState(表示加载的过程0-1-2)未加载-正在加载-加载完成
reader.result 读取完成后的文件内容,可能是字符串或者arraybuffer实例
reader.onabort
reader.onerror
reader.onload
reader.onloadstart
reader.onloadend
reader.onprogress
eg
// HTML 代码如下
// <input type="file" onchange="onChange(event)">
function onChange = (e) => {
let file = e.target.files[0];
let reader = new FileReader();
reader.readAsText(file);
reader.onload = (event) => {
console.log(event.target.result);
};
}
FileReader 有以下实例方法。
- FileReader.abort():终止读取操作,
readyState
属性将变成2
。 - FileReader.readAsArrayBuffer():以 ArrayBuffer 的格式读取文件,读取完成后
result
属性将返回一个 ArrayBuffer 实例。 - FileReader.readAsBinaryString():读取完成后,
result
属性将返回原始的二进制字符串。 - FileReader.readAsDataURL():读取完成后,
result
属性将返回一个 Data URL 格式(Base64 编码)的字符串,代表文件内容。对于图片文件,这个字符串可以用于<img>
元素的src
属性。注意,这个字符串不能直接进行 Base64 解码,必须把前缀data:*/*;base64,
从字符串里删除以后,再进行解码。 - FileReader.readAsText():读取完成后,
result
属性将返回文件内容的文本字符串。该方法的第一个参数是代表文件的 Blob 实例,第二个参数是可选的,表示文本编码,默认为 UTF-8。
下面是一个例子。
/* HTML 代码如下
<input type="file" onchange="previewFile()">
<img src="" height="200">
*/
function previewFile() {
var preview = document.querySelector('img');
var file = document.querySelector('input[type=file]').files[0];
var reader = new FileReader();
reader.addEventListener('load', function () {
preview.src = reader.result;
}, false);
if (file) {
reader.readAsDataURL(file);
}
}
上面代码中,用户选中图片文件以后,脚本会自动读取文件内容,然后作为一个 Data URL 赋值给<img>
元素的src
属性,从而把图片展示出来。