1. 长连接
- 浏览器向服务器进行一次HTTP会话访问后,并不会直接关闭这个连接,而是会默认保持一段时间,那么下一次浏览器继续访问的时候就会再次利用到这个连接;
- 长连接是在http协议下类似摆脱【先请求后响应】模型的一种方式,实现了服务器长时间主动向客户端通信的效果;
- 原生ajax请求实现服务器长连接原理,就是保持ajax readyState状态码一直为3的状态,当允许其继续变化为4时,本次连接就结束了;(具体ajax状态码的知识可以参考另一篇文章使用Promise对象实现ajax封装 - 掘金 (juejin.cn))
- 在ajax请求进行到第四步时,如果xhr.readyState的值是3就一直响应请求,实现一次请求多次响应的效果;
// 1. 建立XMLHttpRequest对象
let xhr = new XMLHttpRequest();
console.log('new过之后', xhr.readyState);
// 2. 配置请求信息,(如open,get方法),使用open方法与服务器建立链接
xhr.open('get', '/readystate');
console.log('open之后', xhr.readyState);
// 3. 向服务器发送数据
xhr.send();
console.log('send之后', xhr.readyState);
// 4. 调用onreadystatechange函数,当xhr.readyState的值是3,就一直响应请求
xhr.onreadystatechange = function () {
console.log('changed', xhr.readyState);
if (xhr.readyState === 3) {
console.log('页面更新了...');
// 响应中
view.innerHTML = xhr.responseText;
}
}
2. 文件的上传、下载、切割(分片)、合并
首先将我们自己封装的ajax请求函数拿过来
function req({
method = 'get',
url,
data,
headers
}) {
return new Promise((resolve, reject) => {
// 原生ajax4步曲
let xhr = new XMLHttpRequest();
xhr.open(method, url);
for (let key in headers) {
xhr.setRequestHeader(key, heades[key]);
}
if (data) xhr.send(data);
else xhr.send();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
resolve(xhr.response);
} else {
reject(xhr.response);
}
}
}
})
}
2.1 文件的上传
- 使用form标签的同步(浏览器)上传 要注意指定form的action、action、enctype属性
<form action="/upload" action="post" enctype="multipart/form-data">
<input type="file" name="file" >
<button>上传1</button>
</form>
- ajax异步上传
可以指定传单个文件或者多个文件
- 拿到要处理的文件let f1 = file2.files[0];
- 创建容器new FormData(),用来存放要处理的文件
- 将要处理的文件添加到容器fd.append('file', f1)
- 调用ajax请求函数进行ajax异步上传(指定访问的url就是实现upload功能的接口)
// html 测试代码
<h2>ajax异步上传</h2>
<input multiple type="file" name="file2" id="file2" >
<button id="ajaxAsyncUpload">ajax异步上传</button>
// 点击ajax异步上传按钮,实现ajax异步上传
ajaxAsyncUpload.onclick = async function () {
let f1 = file2.files[0];
// 创建容器
let fd = new FormData();
// 上传文件
fd.append('file', f1);
let res = await req({
url: '/upload',
method: 'post',
data: fd
});
console.log('异步ajax上传:', res)
}
2.2 文件的下载
- 使用a标签的同步(浏览器)下载 要注意指定a标签的的href、download属性
< !--同步(浏览器)下载-- >
<h2>同步(浏览器)下载</h2>
<a href="/README.md" download="readme.md">同步(浏览器)下载</a>
- 利用a标签实现ajax异步下载
- 点击下载按钮,调用ajax请求获取文件,这里要指定响应数据类型responseType: 'blob',得到二进制文件名,拼接出完整的下载url;
- 在页面新增一个临时的a标签,并指定其href、download属性,让其实现下载功能(为了保持页面不会由于增删a标签而导致页面混乱,指定a标签display属性为none,即不显示在页面上);
- 触发a标签onclick函数,实现下载;
- 触发a标签的remove函数,删除临时创建的a标签;
- 为了优化内存,要注意在最后释放掉我们创建的blob,window.URL.revokeObjectURL(url);
// html 测试代码
<h2>异步下载</h2>
<button id="asyncDownload">ajax下载</button>
// 点击异步下载按钮
asyncDownload.onclick = async function () {
// 前端封装的一个文件容器
let blobRes = await req({
url: '/README.md',
// text 文件arraybuffer blob
responseType: 'blob'
});
console.log(blobRes, 'blobRes');
downloadForTag(blobRes, '我的下载.md')
}
// 利用a标签下载
function downloadForTag(blob, filename) {
let url = window.URL.createObjectURL(blob);
// 创建一个a标签,赋值其url
let a = document.createElement('a');
a.href = url;
// 不指定download 就跳转了
a.download = filename;
a.style.display = 'none';
// 插入元素
document.body.appendChild(a);
// 点击a标签
a.click();
// 释放资源
a.remove();
// 只在当前页签的内存中存在,
// 释放blob:http://localhost:4567/bb34abf7-5ed0-442e-936b-6a15e9489f1e
window.URL.revokeObjectURL(url);
console.log(url, '内存地址指向文件');
}
2.3 文件的切割(分片)
- 调用ajax请求获取文件,这里要指定响应数据类型responseType: 'blob',得到二进制文件名;
- 获取其arraybuffer属性,let arraybuffer = await res.arrayBuffer();
- 根据arraybuffer.byteLength(即文件字节长度)对文件进行切割
- 最后分片下载
// html 测试代码
<h2>文件切割</h2>
<button id="fileSplit">文件切割</button>
// 点击文件切割按钮,实现文件分片功能
fileSplit.onclick = async function () {
let res = await req({
url: '/chunkfile',
responseType: 'blob'
});
let arraybuffer = await res.arrayBuffer();
// 50
let index = 1;
let len = arraybuffer.byteLength / 2;
for (let i = 0; i < arraybuffer.byteLength; i += len) {
downloadForTag(new Blob([arraybuffer.slice(i, i + len)]), `切片${index++}.txt`)
}
}
2.4 文件的合并
- 调用ajax请求获取文件,这里要指定响应数据类型responseType: 'blob',得到二进制文件名;
- 获取要拼接文件的arraybuffer属性,let arraybuffer = await res.arrayBuffer();
- 用new Blob()进行文件拼接
// html 测试代码
<h2>文件合并</h2>
<button id="fileJoin">文件合并</button>
// 点击文件合并按钮,实现文件合并功能
fileJoin.onclick = async function () {
let res = await req({
url: '/chunkfile',
responseType: 'blob'
});
let arraybuffer = await res.arrayBuffer();
// 拼接成双倍
downloadForTag(new Blob([arraybuffer, arraybuffer]), '拼接.txt')
}