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 相关文章推荐
jquery重复提交请求的原因浅析
May 23 Javascript
js自动生成的元素与页面原有元素发生堆叠的解决方法
Sep 04 Javascript
JS限制文本框只能输入数字和字母方法
Feb 28 Javascript
JS使用ajax从xml文件动态获取数据显示的方法
Mar 24 Javascript
JavaScript实现信用卡校验方法
Apr 07 Javascript
js实现网页多级级联菜单代码
Aug 20 Javascript
JavaScript中的函数(二)
Dec 23 Javascript
jQuery插件autocomplete使用详解
Feb 04 Javascript
javascript 缓冲运动框架的实现
Sep 29 Javascript
web前端vue之CSS过渡效果示例
Jan 10 Javascript
基于vue实现网站前台的权限管理(前后端分离实践)
Jan 13 Javascript
关于在vue 中使用百度ueEditor编辑器的方法实例代码
Sep 14 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
优化NFR之一 --MSSQL Hello Buffer Overflow
2006/10/09 PHP
$_GET['goods_id']+0 的使用详解
2013/06/06 PHP
php 使用GD库为页面增加水印示例代码
2014/03/24 PHP
php smarty模板引擎的6个小技巧
2014/04/24 PHP
ThinkPHP实现将本地文件打包成zip下载
2014/06/26 PHP
php源码 fsockopen获取网页内容实例详解
2016/09/24 PHP
PHP使用new StdClass()创建空对象的方法分析
2017/06/06 PHP
PHP实现的文件浏览器功能简单示例
2019/09/12 PHP
PHP设计模式(五)适配器模式Adapter实例详解【结构型】
2020/05/02 PHP
对javascript的一点点认识总结《javascript高级程序设计》读书笔记
2011/11/30 Javascript
jquery自定义函数的多种方法
2014/01/09 Javascript
js判断为空Null与字符串为空简写方法
2014/02/24 Javascript
JQuery boxy插件在IE中边角图片不显示问题的解决
2015/05/20 Javascript
Node.js利用debug模块打印出调试日志的方法
2017/04/25 Javascript
JavaScript截屏功能的实现代码
2017/07/28 Javascript
React Native之TextInput组件解析示例
2017/08/22 Javascript
IntelliJ IDEA 安装vue开发插件的方法
2017/11/21 Javascript
解决VUE中document.body.scrollTop为0的问题
2018/09/15 Javascript
微信小程序实现预览图片功能
2020/10/22 Javascript
React Native 混合开发多入口加载方式详解
2019/09/23 Javascript
Vue+webpack实现懒加载过程解析
2020/02/17 Javascript
[33:39]DOTA2上海特级锦标赛C组小组赛#2 LGD VS Newbee第二局
2016/02/27 DOTA
[04:45]DOTA2-DPC中国联赛正赛 iG vs LBZS 赛后选手采访
2021/03/11 DOTA
python爬虫入门教程之点点美女图片爬虫代码分享
2014/09/02 Python
Django框架下在URLconf中指定视图缓存的方法
2015/07/23 Python
Python 3.x读写csv文件中数字的方法示例
2017/08/29 Python
python微信公众号开发简单流程
2018/03/23 Python
css3圆角边框和边框阴影示例
2014/05/05 HTML / CSS
HTML5之SVG 2D入门11—用户交互性(动画)介绍及应用
2013/01/30 HTML / CSS
输入N,打印N*N矩阵
2012/02/20 面试题
业务经理的岗位职责
2013/11/16 职场文书
给校长的建议书500字
2014/05/15 职场文书
2014市府办领导班子“四风问题”对照检查材料思想汇报
2014/09/24 职场文书
小学感恩节活动策划方案
2014/10/06 职场文书
2019新学期家长会工作计划
2019/08/21 职场文书
Redis超详细讲解高可用主从复制基础与哨兵模式方案
2022/04/07 Redis