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实现将xml转换成html table表格的方法
Apr 17 Javascript
js验证真实姓名与身份证号是否匹配
Oct 13 Javascript
AngularJS基础 ng-cloak 指令简单示例
Aug 01 Javascript
浅谈Angular2 ng-content 指令在组件中嵌入内容
Aug 18 Javascript
vue 粒子特效的示例代码
Sep 19 Javascript
JS实现利用两个队列表示一个栈的方法
Dec 13 Javascript
微信小程序实现聊天对话(文本、图片)功能
Jul 06 Javascript
微信小程序自定义音乐进度条的实例代码
Aug 28 Javascript
小程序最新获取用户昵称和头像的方法总结
Sep 23 Javascript
微信小程序实现多行文字超出部分省略号显示功能
Oct 23 Javascript
JavaScript onclick事件使用方法详解
May 15 Javascript
vue数据字典取键值项目的字典问题
Apr 12 Vue.js
基于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为什么选mysql作为数据库? Mysql 创建用户方法
2007/07/02 PHP
php三维数组去重(示例代码)
2013/11/26 PHP
PHP中预定义的6种接口介绍
2015/05/12 PHP
php集成动态口令认证
2016/07/21 PHP
javascript Zifa FormValid 0.1表单验证 代码打包下载
2007/06/08 Javascript
javascript获取鼠标位置部分的实例代码(兼容IE,FF)
2013/08/05 Javascript
JavaScript显示当前文档最后修改日期的方法
2015/03/19 Javascript
javascript组合使用构造函数模式和原型模式实例
2015/06/04 Javascript
MVC Ajax Helper或Jquery异步加载部分视图
2015/11/29 Javascript
修改js confirm alert 提示框文字的简单实例
2016/06/10 Javascript
JavaScript数组去重的几种方法效率测试
2016/10/23 Javascript
JavaScript轮播图简单制作方法
2017/02/20 Javascript
NodeJS学习笔记之Module的简介
2017/03/24 NodeJs
AngularJS基于provider实现全局变量的读取和赋值方法
2017/06/28 Javascript
详解Express笔记之动态渲染HTML(新手入坑)
2018/12/13 Javascript
JavaScript中var的重要性实例分析
2019/07/09 Javascript
ES6学习笔记之字符串、数组、对象、函数新增知识点实例分析
2020/01/22 Javascript
JavaScript观察者模式原理与用法实例详解
2020/03/10 Javascript
vuex管理状态仓库使用详解
2020/07/29 Javascript
浅析python打包工具distutils、setuptools
2018/04/20 Python
Python实现模拟浏览器请求及会话保持操作示例
2018/07/30 Python
使用GitHub和Python实现持续部署的方法
2019/05/09 Python
深入了解Django中间件及其方法
2019/07/26 Python
python实现登录密码重置简易操作代码
2019/08/14 Python
python统计文章中单词出现次数实例
2020/02/27 Python
PyQt5-QDateEdit的简单使用操作
2020/07/12 Python
python如何编写类似nmap的扫描工具
2020/11/06 Python
Bose法国官网:购买耳机、扬声器、家庭影院、专业音响
2017/12/21 全球购物
有趣、实用和鼓舞人心的产品:Inspire Uplift
2019/11/05 全球购物
Ruby如何进行文件操作
2014/07/17 面试题
汽车检测与维修个人求职信
2013/09/24 职场文书
给老婆道歉的话
2015/01/20 职场文书
2019暑假阅读倡议书
2019/06/24 职场文书
Prometheus 监控MySQL使用grafana展示
2021/08/30 MySQL
美元符号 $
2022/02/17 杂记
Nginx实现负载均衡的项目实践
2022/03/18 Servers