文章目录
前言
- 在另一篇文章JavaScript高级 - nodejs+koa2实现文件上传大文件切片上传断点续传(服务器端)中已经介绍了文件上传的服务器端实现,并实现了几个不同上传形式的服务端接口
- 接下来这篇文章,我们将一步步实现前端(客户端)的上传功能。关于html和css不再过多讲述,这里主要说一下js代码实现。
- 下面先来看一下效果图
一、环境准备及依赖库
- axios v0.21.1: 用于调用服务端接口发送服务端请求
- spark-md5 v3.0.1: 用于根据文件内容生成hash码
- qs v6.9.6:用于将application/x-www-form-urlencoded格式从参数解析为a=x&b=y的格式
二、项目结构
web 项目根目录
- scripts 存放js脚本目录
- axios.min.js axios库(第三方)
- qs.js qs库(第三方)
- spark-md5.min.js spark-md5库(第三方)
- axios2.js axios二次封装库(自定义)、
- upload.js 上传文件功能代码(自定义)
- css 样式文件目录
- upload.css 页面样式
- index.html 文件上传html页面
三、 功能实现
- 结合上面的截图,将分为5个模块进行讲解,所有模块用到的上传控件都是html原生的类型为file的input控件。
- 为了页面的美观,我们将input隐藏起来,并用普通按钮替代,当点击按钮时触发input的click事件。
- 另外可以再额外加一些进度显示,图片缩略图显示,文件名称显示等。
- 关于HTML和css部分不再过多说明,下面将分模块进行js部分重点讲解,每个模块都用闭包函数包裹,避免变量冲突
1、axios二次封装
在每个功能模块中,我们都将通过使用axios向服务端发送请求,这时我们就需要对axios做一些特殊处理,也就是二次封装
- 创建axios对象,避免不同场景配置冲突
- 设置baseURL
- 设置默认Content-Type 为 multipart/form-data
- 在transformRequest中判断Content-Type,如果是application/x-www-form-urlencoded 则利用qs库对参数进行格式化
//axios.js axios二次封装
let request = axios.create();
request.defaults.baseURL = 'http://127.0.0.1:3000';
request.defaults.headers['Content-Type'] = 'mutipart/form-data';
request.defaults.transformRequest = (data, headers) => {
let contentType = headers['Content-Type'];
if (contentType === 'application/x-www-form-urlencoded') return Qs.stringify(data);
return data;
}
request.interceptors.response.use(response => {
return response.data;
});
2、 单文件上传FROM-DATA,先选文件再上传
- 简单步骤分析:
- 首先应该先获取到需要用到的页面元素:上传控件input,选择按钮,上传按钮,缩略图展示,文件展示,进度条展示
- 绑定选择按钮的click事件并在click事件中触发上传控件input的click事件
- 绑定上传控件input的change事件,在该事件中获取已选择的文件
- 绑定上传按钮的click事件,在该事件中组合参数并发送post请求调用服务端API实现文件上传
- 文件上传的关键代码就是发送请求前的参数拼接部分
- 这里我们利用js内置的FromData类将文件作为参数传输
- new FormData().append(“file”, file);
- 代码实现
//upload.js 单文件上传form-data
(function () {
let upload1 = document.querySelector("#upload1"),
upload_inp = upload1.querySelector('.upload-inp'),
upload_select = upload1.querySelector('.upload-btn.select'),
upload_upload = upload1.querySelector('.upload-btn.upload'),
sel_files = upload1.querySelector('.files'),
file1 = upload1.querySelector('.abbr'),
cur_pro = upload1.querySelector('.cur-pro'),
pro_val = upload1.querySelector('.pro-val'),
progress = upload1.querySelector('.progress'),
_file;
upload_select.addEventListener('click', () => {
upload_inp.click();
});
upload_inp.addEventListener('change', function () {
let file = this.files[0];
_file = file;
sel_files.innerHTML = file.name;
progress.style.display = 'inline-block';
pro_val.innerHTML = '';
})
upload_upload.addEventListener('click', function () {
let formData = new FormData();
formData.append('file', _file);
formData.append('filename', _file.name);
request.post('/upload_single_file', formData, {
onUploadProgress: function (ev) {
let pro = ((ev.loaded / ev.total) * 100).toFixed(0) + '%';
cur_pro.style.width = pro;
pro_val.innerHTML = pro;
}
}).then(res => {
console.log(res);
file1.src = `http://${
res.serverPath}`;
file1.style.display = 'block';
}).catch(err => {
console.log(err);
});