• ajax上传文件显示进度


    下面要做一个ajax上传文件显示进度的操作,文末有演示地址


    这里先上代码:

    1、前端代码

      upload.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>ajax-upload</title>
      <link href="https://cdn.bootcss.com/bootstrap/4.0.0/css/bootstrap.css" rel="stylesheet">
      <style>
        .progress-area {
          padding: 20px;
        }
      </style>
    </head>
    <body>
      <div class="">
        <p>文件上传</p>
        <div>
          <input type="file" name="file" id="file" />
          <div>
            <p>图片预览(如果上传文件时图片)</p>
            <div class="img-preview">
            </div>
          </div>
          <button id="upload">上传</button>
          <button id="btn">终止上传</button>
          <div class="progress-area">
            进度
            <div class="progress">
              <div class="progress-bar" id="progress" role="progressbar"  aria-valuenow="25" aria-valuemin="0" aria-valuemax="100">0%</div>
            </div>
            <div>
              <p id="time"></p>
            </div>
          </div>
        </div>
      </div>
      <script>
        (function () {
            'use strict';
            var file = document.querySelector('#file');
            var upload = document.querySelector('#upload');
            var progress = document.querySelector('#progress');
            var time = document.querySelector('#time');
            var imgPreview = document.querySelector('.img-preview');
            var xhr = new XMLHttpRequest();
            var loaded = 0, ot = 0, total = 0, oloaded = 0 ;//;
            upload.addEventListener('click', uploadFile, false);
            file.addEventListener('change', previewImage, false);
            // 点击上传
            function uploadFile(event) {
              if(!file.files[0]) {
                alert('请选择文件')
                return
              }
              if(file.files[0].size>10000000) {
                alert('文件不得超过10M')
                return
              } 
              var formData = new FormData();
              formData.append('test-upload', file.files[0]);
              xhr.onload = uploadSuccess;
              xhr.upload.onprogress = setProgress;
              xhr.open('post', '/upload', true);
              xhr.send(formData);
            }
            // 成功上传
            function uploadSuccess(event) {
              if (xhr.readyState === 4 && xhr.status === 200) {
                setTimeout(()=> {
                  alert('上传成功')
                  window.location.reload();
                },1000)
              }
            }
            // 进度条
            function setProgress(event) {
              // event.total是需要传输的总字节,event.loaded是已经传输的字节。如果event.lengthComputable不为真,则event.total等于0
              if (event.lengthComputable) {//
                  loaded = event.loaded
                  total = event.total
                  var complete = (event.loaded / event.total * 100).toFixed(1);
                  progress.innerHTML = Math.round(complete) + "%";
                  progress.style.width = complete + '%';
              }
              var time = document.getElementById("time");
              var nt = new Date().getTime();//获取当前时间
              var pertime = (nt-ot)/1000; //计算出上次调用该方法时到现在的时间差,单位为s
              ot = new Date().getTime(); //重新赋值时间,用于下次计算
              var perload = event.loaded - oloaded; //计算该分段上传的文件大小,单位b       
              oloaded = event.loaded;//重新赋值已上传文件大小,用以下次计算
              //上传速度计算
              var speed = perload/pertime;//单位b/s
              var bspeed = speed;
              var units = 'b/s';//单位名称
              if(speed/1024>1){
                  speed = speed/1024;
                  units = 'k/s';
              }
              if(speed/1024>1){
                  speed = speed/1024;
                  units = 'M/s';
              }
              speed = speed.toFixed(1);
              //剩余时间
              var resttime = ((event.total-event.loaded)/bspeed).toFixed(1);
              time.innerHTML = '传输速度:'+speed+units+',剩余时间:'+resttime+'s';
              // if(bspeed==0) time.innerHTML = '上传已取消';
            }
            // 图片预览
            function previewImage(event) {
              imgPreview.innerHTML = '' 
              // 每次重新选择文件的时候,都会去除上次选择产生的img标签
              var isImg = (event.target.files[0].type).indexOf('image/') > -1;
              if(isImg) {
                // 如果是图片 就解析图片预览
                var img = document.createElement('img')
                imgPreview.appendChild(img)
                var reader = new FileReader();
                reader.onload = function (event) {
                  img.src = event.target.result;
                  img.width = '200'
                };
                reader.readAsDataURL(event.target.files[0]);
              } else {
                // imgPreview.appendChild('<img src=""/>')
                 // 可以为非图片文件选择一个默认的文件logo
              }
            }
            //++++++++++++++++++++++++++++++++++++++++++++
            xhr.onloadstart = function(){
              console.log("上传开始");    
            }
            btn.onclick = function(){
              xhr.abort();
              console.log("上传被终止.");
              progress.style.width = 0 + '%';
              progress.innerHTML = 0 + '%';
              // 这里调用取消上传的代码
            };
            xhr.ontimeout = function(){
              console.log('上传超时.');
            }
            // xhr.timeout = 50000; // 默认为0 没有时间限制
            // xhr.onabort = function(){
            //   console.log("The transfer has been canceled by the user.");
            // }
            xhr.onerror = function(){
              console.log("上传错误,可能是断网了,也可能是断请求服务了.");  // 这里存在异步传输问题
              return
            }
            xhr.onloadend = function(){
              console.log("请求结束"); // 发送上传的请求,至于有没有上传成功,不清楚,可能失败 成功,这里只是请求结束了 
            }
            // +++++++++++++++++++++++++++++++++++++++++++
          })();
      </script>
    </body>
    </html>
    

      

    2、后端接口(nodejs)

      app.js

       const express = require('express');
        const upload = require('multer')({ dest: 'uploads/' });
        const path = require('path');
        const fs = require('fs');
        const port = 8088;
        
        let  app = express();
        
        app.set('port', port);
        // index.html, index.js放在static文件夹中
        app.use(express.static(path.join(__dirname, 'static')));
        
        // app.get('/', (req, res) => {
        //   res.redirect('upload2.html');
        // });
        
        // 路由/ajax-upload 就回渲染 upload.html 页面
        app.get('/ajax-upload', function(req, res){  
          res.sendFile('upload.html', { root: __dirname });  
        });  
        app.post('/upload', upload.single('test-upload'), (req, res) => {
          // 没有附带文件
          if (!req.file) {
            res.json({ ok: false });
            return;
          }// 重命名文件
          let oldPath = path.join(__dirname, req.file.path);
          let newPath = path.join(__dirname, 'uploads/' + req.file.originalname);
          fs.rename(oldPath, newPath, (err) => {
            if (err) {
              res.json({ ok: false });
              console.log(err);
            } else {
              res.json({ ok: true });
            }
          });
        });
        // 这里还没有做上传取消 删除文件的操作
        app.get('/abort', upload.single('test-upload'), (req, res)=>{
          console.log(req, 'abort') // 删除刚才上传的文件
        })
        
        app.listen(port, () => {
          console.log("[Server] localhost:" + port);
        });

    注:接口使用

    • 环境:nodejs
    • 项目结构 
      • static(图片如下) 
        • app.js
        • upload.html
        • uploads
    • 框架包express: npm install express –save
    • 上传包multer :npm install multer –save
    • 启动: node app.js

    文件结构

    以上代码可以直接复制运行:

    3、解释

    3.1.前端

    上传简述: 前端的选择文件,掉后端上传接口,利用ajax技术将文件上传的服务器。前端要知道后端是否上传成功或者状态,就得需要后端返回给我们的状态,最简单的就是上传成功或者失败,再者需要知道进度的就要利用xhr 的进程方法了。
    • ajax 的原理就是利用浏览器的XMLHttRequest 这个对象,因为IE浏览器不是这对象,如果要兼容的话 ,可以封装一个XHR 对象,代码可以看后面的附属部分:
    • XMLHttRequest 对象有多个方法,监控ajax 上传的进度主要利用这些属性,来达到我们的目标
    • 下面是部分属性(方法)介绍,这里是MDN的介绍 xhr 
      • onloadstart 获取开始
      • onprogress 数据传输进行中
      • onabort 获取操作终止
      • onerror 获取失败
      • onload 获取成功
      • ontimeout 获取操作在用户规定的时间内未完成
      • onloadend 获取完成(不论成功与否)

    * 对xhr.upload.onprogress的解释

    这里监控进度主要看这里的属性值的变化,如下图 
    onprogress的属性值
    具体的代码片段解释可以查看我上面的代码注释

    3.2.后端

    这里使用的nodejs的一个接口,之前我的用的是将前端预览图片的base64的字符串传到后端,然后后端解析这个base64的字符串,生成图片,保存在磁盘中,但是会出现问题,进度不太好显示,所以查了相关资料,借用了这里的写法,直接上传文 件;还有一个就是multer包,利用这个进行上传。

    这里项目也是一个后端渲染的方式,将页面渲染好,然后发送给前端。

    4、附属代码:

    4.1.兼容的ajax代码段

    function XMLHttpRequest(){
        if (typeof XMLHttpRequest != "undefined"){
        return new XMLHttpRequest();
        } else if (typeof ActiveXObject != "undefined"){
            if (typeof arguments.callee.activeXString != "string"){
            var versions = [ "MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0",
            "MSXML2.XMLHttp"],
            i, len;
            for (i=0,len=versions.length; i < len; i++){
                try {
                    new ActiveXObject(versions[i]);
                    arguments.callee.activeXString = versions[i];
                break;
                } catch (ex){
                    //跳过
                }
            }
        }
             return new ActiveXObject(arguments.callee.activeXString);
        } else {
            throw new Error("No XHR object available.");
        }
    }

    4.2.nodejs 处理base64字符串

    node包:formidable 
    imgBase64Arr: 前端传值过来的 base64字符串的多张图片的数组

    for(var i = 0;i<imgBase64Arr.length;i++) {
        var imgname    = util.randomStr(); // 随机字符串的方法
        imgname    = 'assets/img/'+ imgname + '.png';
        var base64     = imgBase64Arr[i].replace(/^data:image/w+;base64,/, "");
        //去掉图片base64码前面部分data:image/png;base64
        var dataBuffer = new Buffer(base64, 'base64'); //把base64码转成buffer对象,
        console.log('dataBuffer是否是Buffer对象:'+Buffer.isBuffer(dataBuffer));
        fs.writeFile(imgname,dataBuffer,function(err){//用fs写入文件
          if(err){
              console.log(err);
          }else{
              console.log('图片上传成功!');
          }
        })
        newimgarr.push(imgname.replace("assets",""));
    }

    github地址:https://github.com/adouwt/ajax-upload

    前端上传这块已经封装了一个基于vue的插件,

    GitHub地址:https://github.com/adouwt/vue-upload    

    npm 官网上的数据显示已经有一定的下载量,欢迎大家学习使用,有bug,及时告知于我

     https://www.npmjs.com/package/vue-ajax-upload

    如有错误,敬请指出!

    这里提供了一个demo演示如下, ps:个人服务器存储较小,只是用来展示,后端是个小白,没有做一些文件过滤和后端文件风险校验,大神请绕行哈,还有请手下留情哈!

    推荐文章:

      https://www.cnblogs.com/tianyuchen/p/5594641.html
    戳我

  • 相关阅读:
    一本通1269 有限背包
    python3 threading.Lock() 多线程锁的使用
    Sqlite3错误:Recursive use of cursors not allowed 的解决方案
    linux 常用命令
    90%的人说Python程序慢,5大神招让你的代码像赛车一样跑起来
    python3 使用flask连接数据库出现“ModuleNotFoundError: No module named 'MySQLdb'”
    Navicat Premium12远程连接MySQL数据库
    pymysql pymysql.err.OperationalError 1045 Access denied最简单解决办法
    CentOS7 安装MySQL8修改密码
    CentOS7 升级Openssl的办法
  • 原文地址:https://www.cnblogs.com/adouwt/p/9481127.html
Copyright © 2020-2023  润新知