js 文件异步上传 显示进度条 显示上传速度 预览文件

时间:2021-11-14 20:03:08

通常文件异步提交有几个关键

1.支持拖拽放入文件。2.限制文件格式。3.预览图片文件。4.上传进度,速度等,上传途中取消上传。5.数据与文件同时上传

现在开始笔记:

需要一个最基础的元素<input id="inputFile" type=file multiple="multiple">

一、首先我们需要实现最基本的异步上传功能

//获得input元素的文件

var fileObj = document.getElementById("inputFile").files;

//定义一个表单数据对象

var form = new FormData();

//将文件input的文件信息放入表单数据对象中

for(var i in fileObj)
{
        form.append("file[]", fileObj[i]); // 文件对象
 }

//定义一个xhr对象

var xhr = new XMLHttpRequest() || new ActiveXObject("Microsoft.XMLHTTP");

//创建一个http请求

xhr.open("post",url,true);

//发送数据

xhr.send(form);

这样数据就上传成功了。

二、接下来说明如何捕获文件上传开始,过程中,结束的各种信息状态

主要是几个xhr回调函数

xhr.onload = function(evt){};//上传请求完成

xhr.onerror = function(evt){};//上传异常

xhr.upload.onloadstart = function(evt){};//开始上传

xhr.upload.onprogress =function(evt){};//上传进度  这个方法会在文件每上传一定字节时调用

evt.loaded//表示已经上传了多少byte的文件大小

evt.total//表示文件总大小为多少byte

通过这两个关键的属性就可以去计算 上传进度与速度

xhr.onreadystatechange = function(){}//当xhr的状态(上传开始,结束,失败)变化时会调用

该方法可以用来在文件上传完成后接收服务器返回的内容  xhr.responceText

中途取消上传 xhr.abort();

三、关于限制文件格式

在fileObj[i]中有个type属性可以使用

四、传文件的时候需要传其他数据

只需要

form.append("name","potatog");

这种键值录入即可。

只是需要注意后台获取数据是 不是在$_FILE中。

五、文件拖放

定义一个用来接收拖放事件的元素

如:

<div id="holder" style="width:150px;height:150px;border:5px solid red;"></div>

//检查浏览器是否支持拖放上传。
  if('draggable' in document.createElement('span')){
    var holder = document.getElementById('holder');
    //当拖放的文件悬浮在该元素上方是触发
    holder.ondragover = function () { this.className = 'hover'; return false; };
    //当拖放的文件不再悬浮在该文件上方时调用
    holder.ondragend = function () { this.className = ''; return false; };
    //当拖放的文件放到该元素中时
    holder.ondrop = function (event) {

      event.preventDefault();

      this.className = '';

      //得到拖放的文件  
      var files = event.dataTransfer.files;

     //将文件放入输入框对象的files中
            fileObj.files = files;
            fileObj.onchange();
      return false;
    };

  }

六、预览图片

fileObj.onchange = function(){
              // 检查是否支持FileReader对象
      if (typeof FileReader != 'undefined') {

//能预览的文件
        var acceptedTypes = {
          'image/png': true,
          'image/jpeg': true,
          'image/gif': true
        };
得到input=file元素中的所有文件信息
        for(var i = 0; i < fileObj.files.length; i++)
        {

//如果是支持预览的图片文件
        if (acceptedTypes[fileObj.files[i].type] === true) {
//新建一个预览对象
          var reader = new FileReader();
//绑定事件 当数据加载后执行  每有一张图片载入完成就会别调用。
          reader.onload = function (event) {
//定义一个图片对象
            var image = new Image();
//将对象载入的结果返回到图片元用来显示
            image.src = event.target.result;

        image.width = 100;

        holder.appendChild(image);

      };

//开始加载数据 文件
        reader.readAsDataURL(fileObj.files[i]);
        }

    }

  }
    }

关于异步上传的封装demo代码

/* pUploadFile.js
 * 使用方法
 * 定义文件异步上传对象
 * var uploadFile = pUploadFile({
            "elId" : "file",
            "previewId" : "show"
        });
 * 定义时必须传递input[type=file]元素的id,
 * preview为选填项  用来预览图片文件的div,即图片文件会以img元素的形式放入preview中
 * 开始上传
 *
 * uploadFile.upload({
 *     
 * });
 *
 *
 * 定义或者执行上传是必须其中一着需要传递参数
 * url 请求地址
 * callback 回调函数名
 *
 */ (function(){
    window.pUploadFile = function(config)
    {
        return new PotatogUploadFile(config);
    }
    
    function PotatogUploadFile(config)
    {
        //文件input元素id
        this.elId = null;
        //input元素对象
        this.elObj = null;
        
        this.progressBoxWidth = 0;
        
        //请求地址
        this.url = null;
        //上传成功后的回调
        this.success = null;
        //上传失败后的回调
        this.faild = null;
        
        //遮罩盒子
        this.progressBox = null;
        
        //百分比
        this.percent = 0;
        
        this.progressStreak = null;
        
        this.xhr = new XMLHttpRequest() || new ActiveXObject("Microsoft.XMLHTTP");
        
        this.startTime = 0;
        
        this.preTime = 0;
        
        this.preLoad = 0;
        
        this.count = 10;
        
        this.previewId = null;
        this.previewObj = null;
        
        this.data = null;
        
        if(typeof config["previewId"] != "undefined")
        {
            this.previewId = config["previewId"];
            this.previewObj = document.querySelector("#previewId");
        }
        
        if(typeof config["elId"] == "undefined")
        {
            console.error("元素ID缺失");
            return this;
        }
        else
        {
            this.elId = config["elId"];
            this.elObj = document.querySelector("#" + this.elId);
            this.inintDom();
        }
        
        this.url = (typeof config.url == "undefined") ? null : config.url;
        this.callback = (typeof config.callback == "undefined") ? null : config.callback;
        this.method = (typeof config.method == "undefined") ? "post" : config.method;
        this.async = (typeof config.async == "undefined") ? true : config.async;
        
    }
    
    //初始化一些交互元素
    PotatogUploadFile.prototype.inintDom = function(){
        var _this = this;
        var inputWidth = this.progressBoxWidth = parseFloat(getComputedStyle(this.elObj).getPropertyValue("width"));
        var inputHeight = parseFloat(getComputedStyle(this.elObj).getPropertyValue("height"));
        
        var inputX = this.elObj.offsetLeft;
        var inputY = this.elObj.offsetTop;
        console.log(inputX,inputY);
        
        //创建一个元素 用来覆盖住input元素
        var progressBox = document.createElement("div");
        progressBox.style.display = "none";
        progressBox.style.position = "absolute";
        progressBox.style.zIndex = "999";
        progressBox.style.left = inputX + "px";
        progressBox.style.top = inputY + "px";
        
        progressBox.style.width = inputWidth + "px";
        progressBox.style.height = inputHeight + "px";
        
        progressBox.style.background = "rgba(58,147,255,0.1)";
        progressBox.style.border = "1px solid rgba(58,147,255,0.3)";
        
        this.progressBox = progressBox;
        this.elObj.parentNode.appendChild(progressBox);
        
        //进度条
        var progressStreak = document.createElement("div");
        progressStreak.style.height = this.progressBox.style.height;
        
        progressStreak.style.background = "rgba(58,147,255,0.2)";
        
        this.progressStreak = progressStreak;
        this.progressBox.appendChild(progressStreak);
        
        //进度条中间的 百分比文字
        var label = document.createElement("label");
        
        label.style.position = "absolute";
        label.style.top = "0px";
        label.style.right = "5px";
        label.style.lineHeight = this.progressBox.style.height;
        label.style.color = "#3B97FF";
        label.style.fontSize = "10px";
        
        this.label = label;
        this.progressBox.appendChild(label);
        
        //右上角的上传速度 剩余时间预计
        var speed = document.createElement("span");
        
        
        speed.style.position = "absolute";
        speed.style.right = "0px";
        speed.style.top = "-12px";
        
        
        speed.style.color = "rgba(58,147,255,0.6)";
        speed.style.fontSize = "10px";
        
        this.speed = speed;
        this.progressBox.appendChild(speed);
        
        var predict = document.createElement("span");
        predict.style.position = "absolute";
        predict.style.left = "0px";
        predict.style.top = "-12px";
        
        predict.style.color = "rgba(58,147,255,0.6)";
        predict.style.fontSize = "10px";
        
        this.predict = predict;
        this.progressBox.appendChild(predict);
        
        
        //取消上传按钮
        var cancel = document.createElement("span");
        cancel.innerHTML = "×";
        cancel.style.position = "absolute";
        
        cancel.style.top = "-5px";
        cancel.style.right = "1px";
        
        cancel.style.color = "#999";
        cancel.style.cursor = "pointer";
        cancel.onclick = function(){
            _this.progressBox.style.display = "none";
            _this.xhr.abort();
        }
        
        this.cancel = cancel;
        this.progressBox.appendChild(cancel);
    }
    
    
    PotatogUploadFile.prototype.setProgressValue = function(percent)
    {
        this.percent = percent;
        this.progressStreak.style.width = Math.ceil(this.progressBoxWidth * percent) + "px";
        this.label.innerHTML = Math.round(this.percent * 100) + "%";
    }
    
    PotatogUploadFile.prototype.setSpeed = function(value)
    {
        if(value > 1000)value = Math.round(value / 1000 / 1024) +  "M/s";
        else value = value + "K/s"
        this.speed.innerHTML = value;
    }
    
    PotatogUploadFile.prototype.setPredict = function(value)
    {
        var m = parseInt(value / 60);
        var s = value % 60;
        this.predict.innerHTML = "预计剩余:" + m + "分 " + s + "秒";
    }
    
    PotatogUploadFile.prototype.upload = function(config)
    {
        if(this.elObj.value == '')return false;
        this.url = (typeof config.url == "undefined") ? null : config.url;
        this.callback = (typeof config.callback == "undefined") ? null : config.callback;
        this.method = (typeof config.method == "undefined") ? "post" : config.method;
        this.async = (typeof config.async == "undefined") ? true : config.async;
        this.data = (typeof config.data == "undefined") ? null : config.data;
        
        this.progressBox.style.display = "block";
        this.initXHR();     }
    
    PotatogUploadFile.prototype.initXHR = function()
    {
        var _this = this;
        this.xhr.open(this.method,this.url,this.async);
        var form = new FormData();
        var files = this.elObj.files;
        if(files.length == 0)
        {
            console.error("未选择文件");
        }
        
        for(var i in files)
        {
            form.append("file",files[i]);
        }
        
        if(_this.data != null)
        {
            for(var key in _this.data)
            {
                form.append("data[" + key + "]",_this.data[key]);
            }
        }
        
        //上传开始
        this.xhr.upload.onloadstart = function()
        {
            _this.startTime = (new Date()).getTime();
            _this.preTime = (new Date()).getTime();
            _this.preLoad = 0;
        };
        
        //上传完成
        this.xhr.onload = function()
        {
            _this.callback(_this.xhr.responseText);
            _this.progressBox.style.display = "none";
        }
        
        //上传失败
        this.xhr.onerror =  function()
        {
            console.log("error");
            _this.progressBox.style.display = "none";
            
        }         //上传进度
        this.xhr.upload.onprogress = function(evt)
        {             _this.setProgressValue(evt.loaded / evt.total);
            
            var nowTime = (new Date()).getTime();
            var nowLoad = evt.loaded;             var timeDelay = (nowTime - _this.preTime) / 1000;
            
            var loadedDelay = nowLoad - _this.preLoad;
            //b/s
            var speed = parseInt(loadedDelay / timeDelay);
            var preTime = Math.round((evt.total - evt.loaded) / speed);
            
            
            _this.count += 1;
            if(_this.count > 10)
            {
                _this.count = 0;
                _this.setSpeed(speed);
                _this.setPredict(preTime);
            }
            _this.preTime = nowTime;
            _this.preLoad = nowLoad;
        }
        this.xhr.send(form);
    }
})(window);

本文参考:

https://www.cnblogs.com/yuanlong1012/p/5127497.html

http://www.ruanyifeng.com/blog/2012/08/file_upload.html