zoukankan      html  css  js  c++  java
  • vue图片上传组件

    前言:很多项目中都需要用到图片上传功能,而其多处使用的要求,为了避免重复造轮子,让我决定花费一些时间去深入了解,最终封装了一个vue的图片上传组件。现将总结再次,希望有帮助。

    Layout

      <div class="upload-wraper">
       <input type="file" id="upload_ele" multiple="false" accept="image/*" @change="uploadFile()" />
    </div>

    type=file将类型设置为选择文件

    multiple是否允许文件的多选

    accept="image/*" 将文件的类型限制为image类型,*包括所有格式的图片

    change事件当type设置为file后,出发的事件为change,也可通过submit实现

    这里布局的话,因为是vue组件所以简单点,不需要多个input构成form表单,然后通过submit提交,一个input通过change事件来实现上传

    Js

    Basic information for uploading files

      let oFIle = document.getElementById('upload-ele').files[0];

    files是input设置为file后的一个js内置对象。files对象死一个read-only属性,不可被修改!

    打印出oFile后可以看到该文件对象的basic information.如下:

    isClosed:false 是否已经结束,可以理解为标签是否闭合

    lastModified:1539602132000最后更改的时间timeStamp

    lastModifiedDate:Mon Oct 15 2018 19:15:32 GMT+0800 (CST) {}最后更改时间

    name:"D9791645A5DF19D17FD7392A080E7A28.jpg"图片的名称

    path:"/Users/mac/Documents/D9791645A5DF19D17FD7392A080E7A28.jpg"图片所在的路径为本地路径

    Size:38938图片的大小信息 单位为kb

    type:'image/jpeg'图片的类型

    webkitRelativePath:""文件相关的路径

    File Size Processing

    大小判断

      (oFile.size / 1024) > 1024

    1M = 1024KB

    Smaller

     let form = new FormData();
      form.append('file',oFile);
      let xhr = new XMLHttpRequest();
      xhr.open('post',url,true);
      xhr.timeout = 30 * 1000;
      xhr.upload.onprogress = this.progress;
      xhr.onload = this.uploadComplete;
      xhr.onerror = this.uploadFailed;
      xhr.upload.onloadstart = () => {
          let date = new Date().getTime();
          let initSize = 0;
      }
      xhr.send(form);

    XMLHttpRequest()是js内置对象,可以使用该属性实现请求头的处理操作。

    xhr.open(); 请求方法: post,url: 服务器接受的地址,true/false 是否异步

    xhr.timeout; 设置超时时间,据情况而定

    xhr.ontimeout; 超时处理,一般为取消请求

    xhr.upload.onprogress; 进程处理 ,上传文件的进度处理

    xhr.onload; 请求成功处理

    xhr.onerror; 请求失败处理

    Xhr.upload.onloadstart; 请求开始处理的操作,一般创建时间戳,初始化大小。

    xhr.send(); 请求配置完毕,发送请求

    这里小于1M的文件是可以直接通过放到formData中,通过配置xhr将图片对象上传到oss,在请求成功时拿到图片的网络路径供后,提供给后端。

    Larger

    why:为什么要对大于1M的图片进行处理呢?因为文件在大于1M的一般是上传失败的。常见的ftp文件上传时,默认是2M,大于时,会上传失败,所以这里的图片上传进行了大于1M压缩的处理。

    how:流程与小于1M时十分相似,不过,这里添加了图片的压缩处理。

    more:一般在较大文件处理时,后端也是可以进行处理的,单独提供较大图片的接口。这时,我们就不需要进行压缩了,只需要在判断大于1024KB时接口更换为处理较大文件的接口即可。

    如何压缩?

    1. 通过FileReader对象将文件对象读取成base64格式。

    2. 通过Image对象结合canvas画布将base格式的图片将一定的比例缩小后重新保存成为base64格式。

    3. 将base64格式转换成Blob流后,图片就可以说是压缩完成了。

    4. 剩下的步骤重复formData,XMLHttpRequest即可完成较大图片的上传。这里需要注意的是,在formData中防止文件时,因为此时是Blob流,所以防止文件时,需要自定义文件格式,这里的处理是 Blob文件 + 时间戳与.jpg组成。

    组件源码

    <template>
      <!-- 图片上传组件 -->
        <div class="upload-wraper">
          <input type="file" id="upload-ele" multiple="false"  accept="image/*" @change="uploadFile(url,quality,hasApi,BigUrl)">
          <toast v-model="total.isShow" type="text">{{total.text}}</toast>
        </div>
      </template>
      <script>
      import { Indicator } from 'mint-ui';
      import { Toast } from 'vux';
      export default {
        name: 'uploadImage',
        components: {
          Indicator,
          Toast,
        },
        props: {
          'url': String, //小与1M的api
          'quality': Number, //图片质量
          'BigUrl': {
            type: String,
            default: '',
          }, //大于1M图片的api
          'hasApi': {
            type: Boolean,
            default: false
          } //是否对大于1M的图片单独分配接口
        },
        data() {
          return {
            total: {isShow:false,text:""}
          }
        },
        methods: {
          uploadFile(url,quality,hasApi,BigUrl) {
            Indicator.open(`上传中`);
            // files是input设置为file后的一个内置对象。files对象是一个只读属性,不可被修改。
            var oFile = document.getElementById('upload-ele').files[0];
            console.log('File Object',oFile);
            var form = new FormData();
            // 大小判断 如果大于1M就新型压缩处理
            // console.log('File Size Unit:KB',(oFile.size / 1024))
            if((oFile.size / 1024) > 1024) {
              if(hasApi) {  
                form.append('file',oFile);
                let xhr = new XMLHttpRequest(); //XMLHttpRequest Object
                xhr.open('post',BigUrl,true); // Method: post,url: server receive address,true/false isAsync
                xhr.timeout = 30 * 1000;  //Timeout one minute;
                xhr.ontimeout = this.uploadTimeout; // Upload Timeout Function
                xhr.upload.onprogress = this.progress; //Progress Function
                xhr.onload = this.uploadComplete; //Upload Success Function
                xhr.onerror = this.uploadFailed; //Upload Failed Funciton
                xhr.upload.onloadstart = () => {
                  let date = new Date().getTime(); // TimeStamp Prevents Caching
                  let initSize = 0; // Init File Size Zero
                } // Upload Start
                xhr.send(form);
              } else {
                this.imgCompress(oFile,{quality: quality},
                (base64Codes) => {
                  var bl = this.convertBase64UrlToBlob(base64Codes);
                  form.append("file", bl, "file_" + Date.parse(new Date()) + ".jpg"); // 文件对象
                  console.log(form);
                  let xhr = new XMLHttpRequest(); // XMLHttpRequest 对象
                  xhr.open("post", url, true); //post方式,url为服务器请求地址,true 该参数规定请求是否异步处理。
                  xhr.upload.onprogress = this.progress; //Progress Function
                  xhr.ontimeout = this.uploadTimeout; // Upload Timeout Function
                  xhr.onload = this.uploadComplete; //Upload Success Function
                  xhr.onerror = this.uploadFailed; //Upload Failed Funciton
                  xhr.upload.onloadstart = function() {
                    let ot = new Date().getTime(); // TimeStamp Prevents Caching
                    let oloaded = 0; // Init File Size Zero
                  };// Upload Start
                  xhr.send(form); 
                })
              }
            } else {
              // 小与1M
              form.append('file',oFile);
              let xhr = new XMLHttpRequest(); //XMLHttpRequest Object
              xhr.open('post',url,true); // Method: post,url: server receive address,true/false isAsync
              xhr.timeout = 30 * 1000;  //Timeout one minute;
              xhr.ontimeout = this.uploadTimeout; // Upload Timeout Function
              xhr.upload.onprogress = this.progress; //Progress Function
              xhr.onload = this.uploadComplete; //Upload Success Function
              xhr.onerror = this.uploadFailed; //Upload Failed Funciton
              xhr.upload.onloadstart = () => {
                let date = new Date().getTime(); // TimeStamp Prevents Caching
                let initSize = 0; // Init File Size Zero
              } // Upload Start
              xhr.send(form); 
            }
          },
          /**
           * @description Request Success
           */
          uploadComplete(evt) {
            let res = JSON.parse(evt.target.responseText);
            if(evt.target.readyState == 4 && evt.target.status == 200) {
              this.$emit('upload',res.result.url);
            } else {
              this.uploadFailed();
            }
          },
          /**
           * @description Request Failed
           */
          uploadFailed(evt) {
            Indicator.close();
            this.total = {
              isShow:true,
              text:"上传失败"
            }
          },  
        /**  
         * @description Timeout Function  
         */  
        uploadTimeout(evt) {  
          this.cancleUploadFile(evt)  
          Indicator.close();  
          this.total = {  
            isShow:true,  
            text:"请求超时"  
          }  
        },  
        /**e  
         * @description Upload Cancel  
         */  
        cancleUploadFile(evt) {  
          evt.abort();  
        },  
        /**  
         * @description Requst Loading....  
         */  
        progress(progressEvent) {  
          if(!progressEvent.lengthComputable) {  
            this.total = {  
              isShow:true,  
              text:"进度读取失败"  
            }  
            return false;  
          }  
          let precent = Math.floor(100 * progressEvent.loaded / progressEvent.total); //Upload Progress  
          if(precent < 100) {  
            Indicator.open(`上传中${precent}%`);  
          } else {  
            Indicator.close();  
            this.total = {    
              isShow:true,  
              text:"上传成功"  
            }  
          }  
        },  
        /**  
          * @description 图片压缩  
          * @param {Object} file 压缩的文件  
          * @param {Number} width 压缩后端宽度,宽度越小,字节越小  
          */  
        imgCompress(file,width,callBack) {  
          var ready = new FileReader();  
          ready.readAsDataURL(file);  
          ready.onload = () => {  
            this.canvasDataURL(ready.result,width,callBack);  
          }      
        },  
        /**  
         * 将以base64的图片url数据转换为Blob  
         * @param urlData  
         * 用url方式表示的base64图片数据  
         */  
        convertBase64UrlToBlob(urlData) {  
          var arr = urlData.split(","),  
            mime = arr[0].match(/:(.*?);/)[1],  
            bstr = atob(arr[1]),  
            n = bstr.length,  
            u8arr = new Uint8Array(n);  
          while (n--) {  
            u8arr[n] = bstr.charCodeAt(n);  
          }  
          return new Blob([u8arr], { type: mime });  
        },    
        /**  
         * @description 大于1M的图片进行重新绘制压缩  
         */  
        canvasDataURL(path, obj, callback) {  
          var img = new Image();  
          img.src = path;  
          img.onload = () => {  
            // var that = this;  
            // 默认按比例压缩  
            var w = this.width,  
              h = this.height,  
              scale = w / h;  
            w = obj.width || w;  
            h = obj.height || w / scale;  
            var quality = 0.7; // 默认图片质量为0.7  
            //生成canvas  
            var canvas = document.createElement("canvas");  
            var ctx = canvas.getContext("2d");  
            // 创建属性节点  
            var anw = document.createAttribute("width");  
            anw.nodeValue = w;  
            var anh = document.createAttribute("height");  
            anh.nodeValue = h;  
            canvas.setAttributeNode(anw);  
            canvas.setAttributeNode(anh);  
            ctx.drawImage(img, 0, 0, w, h);  
            // 图像质量  
            if (obj.quality && obj.quality <= 1 && obj.quality > 0) {  
              quality = obj.quality;  
            }  
            // quality值越小,所绘制出的图像越模糊  
            var base64 = canvas.toDataURL("image/jpeg", quality);  
            // 回调函数返回base64的值  
            callback(base64);  
          };  
        },  
      }  
    }  
    </script>  
    <style lang="less" scoped>  
      .upload-wraper {  
         100%;  
        height: 100%;  
      }  
    </style>
    这里是公众号项目,所以,这里引入的第三方插件为mint-ui和vux。具体情况视情况而定。

    该组件例外封装了,后端是够对较大图片的处理,如果后端已经进行处理,则直接调用。如果没有处理,则进行压缩处理,

    组件的封装,可灵活修改,还有很多地方仍待修改

    插槽,图片上传后,回显可在组件内部实现。借由slot更加完美。

    该组件限制了图片文件的上传,其他文件则不行。

    引入如下:

     <upload-image class="upload" :quality='.7' :url="$base.uploadUrl" :hasApi="false" @upload='uploadImage'></upload-image>

    参考文献

    1. js内置file(文件)对象。https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file

    2. FormData对象的。https://developer.mozilla.org/en-US/docs/Web/API/FormData/FormData

    3. XMLHttpRequest对象。https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest

    4. Image对象。https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/Image

    5. Canvas画布。内容较多,建议通过学习视频了解。在本文中主要用于大型图片的压缩处理

    6. base64和Blob的转换,百度很多已经封装好的。可直接使用。eg:https://www.cnblogs.com/jiujiaoyangkang/p/9396043.html


    Data: 2018-12-13

    author: Csun

  • 相关阅读:
    均匀分布的随机数
    第三十四章 软件工艺的话题
    第三十三章 个人性格
    MySQL常用命令(三)---最值的搜索
    lnmp环境运行laravel open_basedir restriction in effect 问题
    Host 'XXX' is not allowed to connect to this MySQL server解决方案
    CentOS 7中设置PHP7的Log文件日志
    如何查看Laravel版本号的三种方法
    CentOS 7下安装Composer + Laravel
    LNMP一键安装包
  • 原文地址:https://www.cnblogs.com/bgwhite/p/10123065.html
Copyright © 2011-2022 走看看