Vue实现带进度条的文件拖动上传功能


Posted in Javascript onFebruary 23, 2018

1. 基本界面

<!doctype html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <meta name="viewport"
   content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
 <meta http-equiv="X-UA-Compatible" content="ie=edge">
 <link href="https://cdn.bootcss.com/bootstrap/4.0.0/css/bootstrap.min.css" rel="external nofollow" rel="stylesheet">
 <script src="https://cdn.bootcss.com/vue/2.5.13/vue.min.js"></script>
 <style>
  .dropbox {
   border: .25rem dashed #007bff;
   min-height: 5rem;
  }
 </style>
 <title>Document</title>
</head>
<body>
<div id="app" class="m-5">
 <div class="dropbox p-3">
  <h2 class="text-center">把要上传的文件拖动到这里</h2>
 </div>
</div>
<script>
 new Vue({
  el: '#app',
  data: {},
  methods: {},
  mounted: function () {}
 });
</script>
</body>
</html>

Vue实现带进度条的文件拖动上传功能

2. 检测拖动事件

首先让页面支持文件拖拽,在 Vue 的 mounted() 函数中添加代码:

mounted: function () {
 var dropbox = document.querySelector('.dropbox');
 dropbox.addEventListener('dragenter', this.onDrag, false);
 dropbox.addEventListener('dragover', this.onDrag, false);
 dropbox.addEventListener('drop', this.onDrop, false);
}

当把文件拖动到浏览器的拖动区域时,会触发三种事件:

  1. 文件第一次进入拖动区时,触发 dragenter 事件
  2. 文件在拖动区来回拖拽时,不断触发 dragover 事件
  3. 文件已经在拖动区,并松开鼠标时,触发 drop 事件

实现拖动上传,我们只需要关心 drop 事件。不过另外两个事件也需要监听,目的是阻止浏览器默认行为。如果不阻止,那么把文件拖到浏览器时,浏览器就会自动下载这个文件(默认行为),drop 事件触发不出来。

事件的监听函数添加在 Vue 的 methods 对象中:

methods: {
 uploadFile: function (file) {
  console.log(file);
 },
 onDrag: function (e) {
  e.stopPropagation();
  e.preventDefault();
 },
 onDrop: function (e) {
  e.stopPropagation();
  e.preventDefault();
  var dt = e.dataTransfer;
  for (var i = 0; i !== dt.files.length; i++) {
   this.uploadFile(dt.files[i]);
  }
 }
},

onDrop() 函数中,通过 e.dataTransfer.files 可以拿到用户拖动到浏览器的文件的基本信息,uploadFile() 函数现在只这些信息打印了出来,可以了解到,拖动到浏览器的每个文件都是一个 File 对象:

Vue实现带进度条的文件拖动上传功能

3. 处理拖动事件

现在,我们要给 uploadFile() 函数增加功能,实现拖动文件时,拖动区出现文件名和一个上传进度条。

首先在 Vue 的 data 对象中定义 files 属性,用来保存所有拖动到浏览器中文件的名称。然后在uploadFile() 函数每当被调用时,把文件名和上传进度保存到 files 中:

data: {
 files: []
},
methods: {
 uploadFile: function (file) {
  var item = {
   name: file.name,
   uploadPercentage: 67
  };
  this.files.push(item);
 },
}

上传进度的功能在后面再介绍,先写一个固定值。

相应地,在HTML代码中,用 v-for 关键字显示 files 的每一项:

<div class="dropbox p-3">
 <h2 class="text-center">把要上传的文件拖动到这里</h2>
 <div class="border m-2 d-inline-block p-4" style="width:15rem" v-for="file in files">
  <h5 class="mt-0">{{ file.name }}</h5>
  <div class="progress">
   <div class="progress-bar progress-bar-striped"
     :style="{ width: file.uploadPercentage+'%' }"></div>
  </div>
 </div>
</div>

而且,“把要上传的文件拖动到这里” 的提示只在拖动区没有文件的时候才显示:

<h2 v-if="files.length===0" class="text-center">把要上传的文件拖动到这里</h2>

这样,拖动效果就有了:

Vue实现带进度条的文件拖动上传功能

4. 文件上传

接下来实现真正的文件上传,继续往 uploadFile() 函数添加代码:

uploadFile: function (file) {
 var item = {
  name: file.name,
  uploadPercentage: 67
 };
 this.files.push(item);
 var fd = new FormData();
 fd.append('myFile', file);

 var xhr = new XMLHttpRequest();
 xhr.open('POST', 'upload.php', true);
 xhr.send(fd);
},

这里用到了 FormData,把要上传的文件附在了 FormData 上,并通过AJAX方式发送给PHP端。PHP端代码:

if (isset($_FILES['myFile'])) {
 move_uploaded_file($_FILES['myFile']['tmp_name'], 'uploads/' . $_FILES['myFile']['name']);
 echo 'OK';
} else {
 echo 'No file specified';
}

现在刷新下页面,把电脑上的两个文件拖到浏览器中,PHP端会接收并保存文件到 uploads 目录:

Vue实现带进度条的文件拖动上传功能

提示:如果拖放时遇到PHP返回了No file specified,或者 $_FILES 为 NULL 的情况时,有可能是PHP限制了POST请求最大字节,或者限制了上传文件的体积。这时候需要调整下php.ini中的这两个配置:

post_max_size = 20M // POST请求的最大字节数
upload_max_filesize = 20M // 上传文件的最大体积

进度条的展示

基本的上传功能完成了,最后我们来完成进度条。每当AJAX请求发送了一段时间的数据时,都会生成一个 progress 事件,我们可以监听 progress 事件来知道当前的上传进度:

uploadFile: function (file) {
  ...
  xhr.upload.addEventListener('progress', function (e) {
    item.uploadPercentage = Math.round((e.loaded * 100) / e.total);
  }, false);
  xhr.send(fd);
},

e.loaded 代表当前AJAX发送了多少字节,e.total 代表AJAX总共要发送多少字节。通过这两个属性可以计算上传进度的百分比。

这样,一个带进度条的文件拖动上传功能就完成了。

附:完整代码

<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport"
     content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <link href="https://cdn.bootcss.com/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet">
  <script src="https://cdn.bootcss.com/vue/2.5.13/vue.min.js"></script>
  <style>
    .dropbox {
      border: .25rem dashed #007bff;
      min-height: 5rem;
    }
  </style>
  <title>Document</title>
</head>
<body>
<div id="app" class="m-5">
  <div class="dropbox p-3">
    <h2 v-if="files.length===0" class="text-center">把要上传的文件拖动到这里</h2>
    <div class="border m-2 d-inline-block p-4" style="width:15rem" v-for="file in files">
      <h5 class="mt-0">{{ file.name }}</h5>
      <div class="progress">
        <div class="progress-bar progress-bar-striped"
           :style="{ width: file.uploadPercentage+'%' }"></div>
      </div>
    </div>
  </div>
</div>
<script>
  new Vue({
    el: '#app',
    data: {
      files: []
    },
    methods: {
      uploadFile: function (file) {
        var item = {
          name: file.name,
          uploadPercentage: 0
        };
        this.files.push(item);
        var fd = new FormData();
        fd.append('myFile', file);
        var xhr = new XMLHttpRequest();
        xhr.open('POST', 'upload.php', true);
        xhr.upload.addEventListener('progress', function (e) {
          item.uploadPercentage = Math.round((e.loaded * 100) / e.total);
        }, false);
        xhr.send(fd);
      },
      onDrag: function (e) {
        e.stopPropagation();
        e.preventDefault();
      },
      onDrop: function (e) {
        e.stopPropagation();
        e.preventDefault();
        var dt = e.dataTransfer;
        for (var i = 0; i !== dt.files.length; i++) {
          this.uploadFile(dt.files[i]);
        }
      }
    },
    mounted: function () {
      var dropbox = document.querySelector('.dropbox');
      dropbox.addEventListener('dragenter', this.onDrag, false);
      dropbox.addEventListener('dragover', this.onDrag, false);
      dropbox.addEventListener('drop', this.onDrop, false);
    }
  });
</script>
</body>
</html>

总结

以上所述是小编给大家介绍的Vue实现带进度条的文件拖动上传功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
ASP.NET jQuery 实例8 (动态添加内容到DropDownList)
Feb 03 Javascript
图片延迟加载的实现代码(模仿懒惰)
Mar 29 Javascript
jQuery仿Flash上下翻动的中英文导航菜单实例
Mar 10 Javascript
使用AngularJS创建单页应用的编程指引
Jun 19 Javascript
jquery转盘抽奖功能实现
Nov 13 Javascript
Query常用DIV操作获取和设置长度宽度的实现方法
Sep 19 Javascript
老生常谈javascript的类型转换
Oct 12 Javascript
JS数组操作之增删改查的简单实现
Aug 21 Javascript
laydate日历控件使用方法详解
Nov 20 Javascript
js 实现复选框只能选择一项的示例代码
Jan 23 Javascript
vue 动态组件用法示例小结
Mar 06 Javascript
解决vue-cli输入命令vue ui没效果的问题
Nov 17 Javascript
详解vue2.0 不同屏幕适配及px与rem转换问题
Feb 23 #Javascript
解决vue中对象属性改变视图不更新的问题
Feb 23 #Javascript
在Vue组件上动态添加和删除属性方法
Feb 23 #Javascript
vue 路由页面之间实现用手指进行滑动的方法
Feb 23 #Javascript
vue编译打包本地查看index文件的方法
Feb 23 #Javascript
vue 使用Jade模板写html,stylus写css的方法
Feb 23 #Javascript
Angular 向组件传递模板的两种方法
Feb 23 #Javascript
You might like
ecshop 批量上传(加入自定义属性)
2012/03/20 PHP
php设置编码格式的方法
2013/03/05 PHP
php实现信用卡校验位算法THE LUHN MOD-10示例
2014/05/07 PHP
飞鱼(shqlsl) javascript作品集
2006/12/16 Javascript
javascript 动态设置已知select的option的value值的代码
2009/12/16 Javascript
跟我学习javascript的基本类型和引用类型
2015/11/16 Javascript
javascript HTML5 canvas实现打砖块游戏
2020/06/18 Javascript
Angularjs的ng-repeat中去除重复数据的方法
2016/08/05 Javascript
AngularJS实现表单验证功能
2017/01/09 Javascript
Node.js使用NodeMailer发送邮件实例代码
2017/03/06 Javascript
jQuery实现鼠标经过显示动画边框特效
2017/03/24 jQuery
Angular.js指令学习中一些重要属性的用法教程
2017/05/24 Javascript
3种vue组件的书写形式
2017/11/29 Javascript
Angular4 组件通讯方法大全(推荐)
2018/07/12 Javascript
vue双向绑定及观察者模式详解
2019/03/19 Javascript
Node.js API详解之 assert模块用法实例分析
2020/05/26 Javascript
vue中实现图片压缩 file文件的方法
2020/05/28 Javascript
JS倒计时两种实现方式代码实例
2020/07/27 Javascript
Vue用mixin合并重复代码的实现
2020/11/27 Vue.js
[49:42]DOTA2上海特级锦标赛主赛事日 - 3 胜者组第二轮#2Secret VS EG第一局
2016/03/04 DOTA
使用Python对SQLite数据库操作
2017/04/06 Python
老生常谈Python序列化和反序列化
2017/06/28 Python
用python实现的线程池实例代码
2018/01/06 Python
解决python3 安装不了PIL的问题
2019/08/16 Python
Python 动态导入对象,importlib.import_module()的使用方法
2019/08/28 Python
python3 pillow模块实现简单验证码
2019/10/31 Python
Python基于Socket实现简单聊天室
2020/02/17 Python
python 利用jieba.analyse进行 关键词提取
2020/12/17 Python
python实现控制台输出颜色
2021/03/02 Python
伦敦一卡通:The London Pass
2018/11/30 全球购物
澳大利亚购买最佳炊具品牌网站:Cookware Brands
2019/02/16 全球购物
小学体育教学反思
2014/01/31 职场文书
2014年教育工作总结
2014/11/26 职场文书
2019脱贫攻坚工作总结报告范本!
2019/08/06 职场文书
在Windows Server 2012上安装 .NET Framework 3.5 所遇到的问题
2022/04/29 Servers
纯CSS打字动画的实现示例
2022/08/05 HTML / CSS