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 相关文章推荐
JavaScript中String和StringBuffer的速度之争
Apr 01 Javascript
Javascript的一种模块模式
Sep 08 Javascript
JSChart轻量级图形报表工具(内置函数中文参考)
Oct 11 Javascript
jquery form 加载数据示例
Apr 21 Javascript
iScroll中事件点击触发两次解决方案
Mar 11 Javascript
HTML5 Shiv完美解决IE(IE6/IE7/IE8)不兼容HTML5标签的方法
Nov 25 Javascript
jquery实现可旋转可拖拽的文字效果代码
Jan 27 Javascript
js闭包引起的事件注册问题介绍
Mar 29 Javascript
一道优雅面试题分析js中fn()和return fn()的区别
Jul 05 Javascript
Vue.js实例方法之生命周期详解
Jul 03 Javascript
setTimeout时间设置为0详细解析
Mar 13 Javascript
JS事件绑定的常用方式实例总结
Mar 02 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 无限级 SelectTree 类
2009/05/19 PHP
php empty() 检查一个变量是否为空
2011/11/10 PHP
PHP 获取文件路径(灵活应用__FILE__)
2013/02/15 PHP
PHP 读取Postgresql中的数组
2013/04/14 PHP
php动态读取数据清除最右边距的方法
2017/04/12 PHP
jQuery学习总结之元素的相对定位和选择器(持续更新)
2011/04/26 Javascript
JS阻止冒泡事件以及默认事件发生的简单方法
2014/01/17 Javascript
浅谈Javascript线程及定时机制
2015/07/02 Javascript
CSS或者JS实现鼠标悬停显示另一元素
2016/01/22 Javascript
TypeOf这些知识点你了解吗
2016/02/21 Javascript
jQuery自制提示框tooltip改进版
2016/08/01 Javascript
浅谈箭头函数写法在ReactJs中的使用
2017/08/22 Javascript
Vue 进入/离开动画效果
2017/12/26 Javascript
微信小程序实现自上而下字幕滚动
2018/07/14 Javascript
利用d3.js力导布局绘制资源拓扑图实例教程
2019/01/08 Javascript
vue+render+jsx实现可编辑动态多级表头table的实例代码
2020/04/01 Javascript
Vue通过getAction的finally来最大程度避免影响主数据呈现问题
2020/04/24 Javascript
如何实现vue的tree组件
2020/12/03 Vue.js
[03:47]2015国际邀请赛第三日现场精彩回顾
2015/08/08 DOTA
[57:47]Fnatic vs Winstrike 2018国际邀请赛小组赛BO2 第二场 8.18
2018/08/19 DOTA
Python的ORM框架SQLAlchemy入门教程
2014/04/28 Python
Python实现的百度站长自动URL提交小工具
2014/06/27 Python
基于wxPython的GUI实现输入对话框(1)
2019/02/27 Python
python绘图模块matplotlib示例详解
2019/07/26 Python
python实现横向拼接图片
2020/03/23 Python
Python分析微信好友性别比例和省份城市分布比例的方法示例【基于itchat模块】
2020/05/29 Python
HTML5新增form控件和表单属性实例代码详解
2019/05/15 HTML / CSS
意大利自行车商店:Cingolani Bike Shop
2019/09/03 全球购物
Myprotein亚太地区:欧洲第一在线运动营养品牌
2020/12/20 全球购物
将n个数按输入顺序的逆序排列,用函数实现
2012/11/14 面试题
澳大利亚商务邀请函
2014/01/17 职场文书
单位成立周年感言
2014/01/26 职场文书
小学学雷锋活动总结
2014/04/25 职场文书
教师一帮一活动总结
2014/07/08 职场文书
寒假安全保证书
2015/02/28 职场文书
降价通知函
2015/04/23 职场文书