node.js实现带进度条的多文件上传


Posted in Javascript onMarch 27, 2020

用node.js实现多文件上传并携带进度条的demo,供大家参考,具体内容如下

这个独立封装的需求来自一个朋友公司,他说需要写一个带进度条动画的批量上传文件的组件,结果他们后端跟他说不能多文件上传,我一听就很尴尬了,怎么可能不能多文件呢哈哈,后来我只是告诉他进度条的实现方式,在过了2天后我一直对此事耿耿于怀,所以干脆自己动手用node写了一个多文件上传的demo,并记录下来。

  • 前端: http请求为自己封装的一个原生请求函数,一切以原生代码为主;
  • 后端(nodeJs): express + multer,自定义 multer 包的 diskStorage 函数;

直接上demo吧,我加上一点注解就好,就不用详细说明了, 其中写了一个测试接口用来测试,可以不用管;

// 前端 upload.html
<!DOCTYPE html>
<html>
 <head>
 <meta charset="utf-8">
 <title>上传文件demo</title>
 <style media="screen">
  .progress{
  width: 50%;
  height: 5px;
  border: 1px solid #ccc;
  border-radius: 4px;
  margin-top: 10px;
  position: relative;
  }
  .progress>span{
  display: inline-block;
  position: absolute;
  border-radius: 4px;
  top: 0;
  left: 0;
  height: 100%;
  width: 0;
  background-color: rgb(98, 230, 74);
  transition: width 0.3s ease-out;
  }
 </style>
 </head>
 <body>
 <input id="file" type="file" multiple>
 <div class="progress">
  <span></span>
 </div>
 <script type="text/javascript">
  var http = function (option) {
  // 过滤请求成功后的响应对象
  function getBody (xhr) {
   var text = xhr.responseText || xhr.response
   if (!text) {
   return text
   }

   try {
   return JSON.parse(text)
   } catch (err) {
   return text
   }
  }

  var xhr = new XMLHttpRequest();
  // 自定义 beforeSend 函数
  if(option.beforeSend instanceof Function) {
   if (option.beforeSend(xhr) === false) {
   return false
   }
  }

  xhr.onreadystatechange = function () {
   if (xhr.status === 200) {
   if (xhr.readyState === 4) {
    // 成功回调
    option.onSuccess(getBody(xhr))
   }
   }
  }

  // 请求失败
  xhr.onerror = function (err) {
   option.onError(err)
  }

  xhr.open(option.type, option.url, true)

  // 当请求为上传文件时回调上传进度
  if (xhr.upload) {
   xhr.upload.onprogress = function (event) {
   if (event.total > 0) {
    event.percent = event.loaded / event.total * 100;
   }
   // 监控上传进度回调
   if (option.onProgress instanceof Function) {
    option.onProgress(event)
   }
   }
  }

  // 自定义头部
  const headers = option.headers || {}
  for (var item in headers) {
   xhr.setRequestHeader(item, headers[item])
  }

  xhr.send(option.data)
  }
 
 // 测试接口
  http({
  type: 'POST',
  url: '/test',
  data: JSON.stringify({
   name: 'yolo'
  }),
  onSuccess: function (data) {
   console.log(data)
  },
  onError: function (err) {
   console.log(err)
  }
  })
  document.getElementById('file').onchange = function () {
  var fileList = this.files, formData = new FormData();
  Array.prototype.forEach.call(fileList, function (file) {
   formData.append(file.name, file)
  })
  // 当上传的数据为 file 类型时,请求的格式类型自动会变为 multipart/form-data, 如果头部格式有特定需求,在我的 http 函数中传入 headers<Object> 即可,大家可自己查看,我这里没有什么特殊处理所以就不传了
  http({
   type: 'POST',
   url: '/upload',
   data: formData,
   onProgress: function (event) {
   console.log(event.percent)
   document.querySelector('.progress span').style.width = event.percent + '%';
   },
   onSuccess: function (data) {
   console.log('上传成功')
   },
   onError: function (err) {
   alert(err)
   }
  })
  }
 </script>
 </body>
</html>

后端所用的一些东西我放在这

express中间件-multer
express 4.x 文档

// 后端(node.js) upload.js
var express = require('express');
var path = require('path');
var fs = require('fs');
var app = express();
var bodyParser = require('body-parser'); // 过滤请求头部相应格式的body
var multer = require('multer');
var chalk = require('chalk'); // 只是一个 cli 界面字体颜色包而已
var log = console.log.bind(console);

app.use(express.static('static'));
// 接受 application/json 格式的过滤器
var jsonParser = bodyParser.json()
// 接受 application/x-www-form-urlencoded 格式的过滤器
var urlencodedParser = bodyParser.urlencoded({ extended: false })
// 接受 text/html 格式的过滤器
var textParser = bodyParser.text()

// 自定义 multer 的 diskStorage 的存储目录与文件名
var storage = multer.diskStorage({
 destination: function (req, file, cb) {
 cb(null, 'view')
 },
 filename: function (req, file, cb) {
 cb(null, file.fieldname)
 }
})

var upload = multer({ storage: storage })

// 页面渲染
app.get('/', function (req, res) {
 res.sendFile(path.join(__dirname, 'view/upload.html'));
})

app.post('/test', textParser, jsonParser, function (req, res) {
 log(req.body);
 var httpInfo = http.address();
 res.send({
 host: httpInfo.address,
 port: httpInfo.port
 })
})

// 对应前端的上传接口 http://127.0.0.1:3000/upload, upload.any() 过滤时不对文件列表格式做任何特殊处理
app.post('/upload', upload.any(), function (req, res) {
 log(req.files)
 res.send({message: '上传成功'})
})

// 监控 web 服务
var http = app.listen(3000, '127.0.0.1', function () {
 var httpInfo = http.address();
 log(`创建服务${chalk.green(httpInfo.address)}:${chalk.yellow(httpInfo.port)}成功`)
})

上传完毕后重新上传我没写动画重置,大家实际用的时候肯定是需要展示每个上传文件的,每次上传文件都对应着一个进度条,所以应该抽象为一个组件,至于组件的抽象我这就不详细写了,那个就很容易了。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
XMLHTTP 乱码的解决方法(UTF8,GB2312 编码 解码)
Jan 12 Javascript
防止按钮在短时间内被多次点击的方法
Mar 10 Javascript
JavaScript动态创建link标签到head里的方法
Dec 22 Javascript
BootstrapTable与KnockoutJS相结合实现增删改查功能【二】
May 10 Javascript
js操作浏览器的参数方法
Jan 21 Javascript
JavaScript中的编码和解码函数
Feb 15 Javascript
原生js编写2048小游戏
Mar 17 Javascript
Iphone手机、安卓手机浏览器控制默认缩放大小的方法总结(附代码)
Aug 18 Javascript
改变vue请求过来的数据中的某一项值的方法(详解)
Mar 08 Javascript
深入浅析Vue.js 中的 v-for 列表渲染指令
Nov 19 Javascript
ECharts地图绘制和钻取简易接口详解
Jul 12 Javascript
JavaScript的垃圾回收机制与内存管理
Aug 06 Javascript
基于Express框架使用POST传递Form数据
Aug 10 #Javascript
Vue实现点击显示不同图片的效果
Aug 10 #Javascript
vue+eslint+vscode配置教程
Aug 09 #Javascript
一个手写的vue放大镜效果
Aug 09 #Javascript
详解Vue-cli3.X使用px2rem遇到的问题
Aug 09 #Javascript
微信小程序引入模块中wxml、wxss、js的方法示例
Aug 09 #Javascript
小程序Request的另类用法详解
Aug 09 #Javascript
You might like
php类中的各种拦截器用法分析
2014/11/03 PHP
PHP实现的DES加密解密封装类完整实例
2017/04/29 PHP
phpstudy的php版本自由修改的方法
2017/10/18 PHP
(仅IE下有效)关于checkbox 三态
2007/05/12 Javascript
使用Microsoft Ajax Minifier减小JavaScript文件大小的方法
2010/04/01 Javascript
解析URI与URL之间的区别与联系
2013/11/22 Javascript
使用js判断TextBox控件值改变然后出发事件
2014/03/07 Javascript
javascript解析json数据的3种方式
2014/05/08 Javascript
javascript框架设计读书笔记之数组的扩展与修复
2014/12/02 Javascript
Javascript基础教程之变量
2015/01/18 Javascript
javascript实现动态加载CSS
2015/01/26 Javascript
使用npm发布Node.JS程序包教程
2015/03/02 Javascript
基于jQuery Tipso插件实现消息提示框特效
2016/03/16 Javascript
Vue实现textarea固定输入行数与添加下划线样式的思路详解
2018/06/28 Javascript
JavaScript创建对象方法实例小结
2018/09/03 Javascript
在Vue中实现随hash改变响应菜单高亮
2020/03/09 Javascript
[05:06]DOTA2-DPC中国联赛 正赛 VG vs Magma选手采访
2021/03/11 DOTA
python读取csv文件示例(python操作csv)
2014/03/11 Python
Python中json格式数据的编码与解码方法详解
2016/07/01 Python
解决python文件字符串转列表时遇到空行的问题
2017/07/09 Python
K-means聚类算法介绍与利用python实现的代码示例
2017/11/13 Python
详解python中list的使用
2019/03/15 Python
django ManyToManyField多对多关系的实例详解
2019/08/09 Python
python处理excel绘制雷达图
2019/10/18 Python
numpy np.newaxis 的实用分享
2019/11/30 Python
python os模块常用的29种方法使用详解
2020/06/02 Python
关于tf.matmul() 和tf.multiply() 的区别说明
2020/06/18 Python
Python实现邮件发送的详细设置方法(遇到问题)
2021/01/18 Python
iHerb中文官网:维生素、保健品和健康产品
2018/11/01 全球购物
小学校长竞聘演讲稿
2014/05/16 职场文书
校运动会广播稿300字
2014/10/07 职场文书
企业群众路线教育实践活动心得体会
2014/11/03 职场文书
违反纪律检讨书范文
2015/05/07 职场文书
超级详细实用的pycharm常用快捷键
2021/05/12 Python
分享几种python 变量合并方法
2022/03/20 Python
关于pytest结合csv模块实现csv格式的数据驱动问题
2022/05/30 Python