长连接及文件的上传、下载、切割(分片)、合并

178 阅读4分钟
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 文件的上传
  1. 使用form标签的同步(浏览器)上传 要注意指定form的action、action、enctype属性
<form action="/upload" action="post" enctype="multipart/form-data">
  <input type="file" name="file" >
    <button>上传1</button>
</form>
  1. 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 文件的下载
  1. 使用a标签的同步(浏览器)下载 要注意指定a标签的的href、download属性
< !--同步(浏览器)下载-- >
<h2>同步(浏览器)下载</h2>
<a href="/README.md" download="readme.md">同步(浏览器)下载</a>
  1. 利用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')
}