图片预览、压缩、ajax上传

       现在网页的图片交互越来越多,对于图片的上传、预览、压缩、裁剪等处理,都很常见,自己也查了很多资料,写了一个很常见的图片上传实例,自己的项目中也经常使用,现在总结一下相关的技术,也供后面参考和改进。具体实现效果如下

 

相关内容

1.图片预览

通常上传文件使用input表单来,就是只能显示文件名,现在基本都是通过图片预览,让用户更好的操作。实现预览原理就是将input获取到的文件通过createObjectURL转化成img的src路径,实现图片的展示

<input type="file" accept="image/*"   id="inputfile" @change="preImg('inputfile')" >
getFileUrl:function (id) {//根据输入框id 生成图片src
        console.log(document.getElementById(id).files[0]);
        var url;
        if (navigator.userAgent.indexOf("MSIE")>=1) { // IE
          url = document.getElementById("inputfile").value;
        } else if(navigator.userAgent.indexOf("Firefox")>0) { // Firefox
          url = window.URL.createObjectURL(document.getElementById(id).files.item(0));
        } else if(navigator.userAgent.indexOf("Chrome")>0) { // Chrome
          url = window.URL.createObjectURL(document.getElementById(id).files.item(0));
        }else {
          url = window.webkitURL.createObjectURL(document.getElementById(id).files[0]);
        }
        return url;
      },

 2.图片的上传

和文件上传一样,需要通过formdata进行上传,jquery的ajax上传文件还需要设置mimeType,还需要取消type:'json',因此成功后的内容为字符串格式,需要重新转化成json,里面的subFile是一个存放文件的数组,每个值是input的files值==>document.getElementById(id).files.item(0)或者document.getElementById(id).files[0],根据不同浏览器不同,见上图。

          var formData = new FormData();
          for(var k in this.subFile){ //文件数组
            formData.append('pPath',this.subFile[k]);
          }
          formData.append('type',this.addType);
          formData.append('message',this.addContent);
          formData.append('userNo',JSON.parse(localStorage.getItem('skyUser')).userNo);
          formData.append('token',localStorage.getItem('skyUsertoken'));
          $.ajax({
            type:"post",
            url:vm.path+"makeStatement.json",
            data:formData,
            cache: false,
            contentType: false,
            processData:false,
            mimeType:"multipart/form-data",
            success:function(data){
              var a=JSON.parse(data);       
            }});

3.图片压缩

基于现在手机拍照,照片都很大,严重影响上传速度,因此需要对图片进行压缩上传,具体也是基于canvas的画图canvas.toDataURL('image/jpeg', quality);下面方法就是通过img的src进行图片压缩,obj设置压缩后宽高,callback函数返回图片的base64值。

 dealImage: function (path, obj, callback) {
        var img = new Image();
        img.src = path;
        img.onload = function () {
          var that = this;
          // 默认按比例压缩
          var w = that.width,
              h = that.height,
              scale = w / h;
          w = obj.width || w;
          h = obj.height || (w / scale);
          var quality = 0.9;
          //生成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(that, 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);
        }
      },

  压缩后的图片为base64则需要转化成blob对象才能进行上传,具体实现如下方法

  convertBase64UrlToBlob: function (urlData) {
        var bytes = window.atob(urlData.split(',')[1]);        //去掉url的头,并转换为byte
        //处理异常,将ascii码小于0的转换为大于0
        var ab = new ArrayBuffer(bytes.length);
        var ia = new Uint8Array(ab);
        for (var i = 0; i < bytes.length; i++) {
          ia[i] = bytes.charCodeAt(i);
        }
        return new Blob([ab], {type: 'image/png'});
      },

 因此在压缩的callback里调用

       let vm = this;
       vm.dealImage(src,{width:300},function (base) {
          console.log(vm.convertBase64UrlToBlob(base));
          vm.subFile.push(vm.convertBase64UrlToBlob(base))
          console.log(vm.imgs);
        })

完整代码如下,复制出来直接使用

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
  <meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no" name="viewport">
  <meta content="yes" name="apple-mobile-web-app-capable">
  <meta content="black" name="apple-mobile-web-app-status-bar-style">
</head>
<style>
  .addItem{
    width: 100%;
    height: 100%;
    position: fixed;
    top: 0;
    z-index: 9999;
    font-size: 0.3rem;
    background-color: #fff;
    overflow: scroll;
  }

  .addItem .add_content{
    border-bottom: dashed #fda7f7 1px;
  }

  .cream {
    position: relative;
    display: inline-block;
    overflow: hidden;
    text-decoration: none;
    text-indent: 0;
    margin-top: 10px;
    margin-left: 10px;
    width: 80px;
    height: 80px;
    border-radius: 5px;
    border: solid 1px #eee;
  }
  .cream .addimg{
    font-size: 30px;
    color: #555;
    text-align: center;
    line-height: 80px;
  }
  .cream input {
    position: absolute;
    display: block;
    font-size: 60px;
    right: 0;
    top: 0;
    opacity: 0;
  }
  .cream:hover {
    text-decoration: none;
  }
  .deleteImg{
    position: absolute;
    right: 2px;
    top: 2px;
    width: 20px;
    height: 20px;
    background: rgba(150,150,150,0.5);
    text-align: center;
    line-height: 20px;
    font-size: 20px;
    color: #fff;
  }
</style>
<body>
<div id="main">
  <div id="smallimages">
    <div class="cream" v-for="(img,n) in imgs" :key="n">
      <div class="deleteImg" @click="deleteImg(n)">-</div>
      <img :src="img" width="80px" height="80px">
    </div>
    <div class="cream" v-show="cream">
      <div class="addimg">+</div>
      <input type="file" accept="image/*"   id="inputfile" @change="preImg('inputfile')" >
    </div>
  </div>
</div>
</body>
<script src="http://code.jquery.com/jquery-1.4.1.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
<script>
  var vm = new Vue({
    el:"#main",
    data:function() {
      return {
        addType: '说说',
        cream:true,
        imgs:[],//显示的图片
        subFile:[],//上传的图片
      }
  },
    watch:{//设置最多上传9张
      imgs:function (val,old) {
        if(val.length>=9){
          this.cream = false
        }else {
          this.cream = true
        }
      }
    },
    methods:{
      preImg:function (id) {
        var vm = this;
        var src = this.getFileUrl(id);
        vm.imgs.push(src);
        console.log( this.getFileUrl(id));
        vm.dealImage(src,{width:300},function (base) {
          console.log(vm.convertBase64UrlToBlob(base));
          vm.subFile.push(vm.convertBase64UrlToBlob(base))
          console.log(vm.imgs);
        })
      },
      getFileUrl:function (id) {//根据输入框id 生成图片src
        console.log(document.getElementById(id).files[0]);
        var url;
        if (navigator.userAgent.indexOf("MSIE")>=1) { // IE
          url = document.getElementById("inputfile").value;
        } else if(navigator.userAgent.indexOf("Firefox")>0) { // Firefox
          url = window.URL.createObjectURL(document.getElementById(id).files.item(0));
        } else if(navigator.userAgent.indexOf("Chrome")>0) { // Chrome
          url = window.URL.createObjectURL(document.getElementById(id).files.item(0));
        }else {
          url = window.webkitURL.createObjectURL(document.getElementById(id).files[0]);
        }
        return url;
      },
      dealImage: function (path, obj, callback) {//进行图片压缩
        var img = new Image();
        img.src = path;
        img.onload = function () {
          var that = this;
          // 默认按比例压缩
          var w = that.width,
            h = that.height,
            scale = w / h;
          w = obj.width || w;
          h = obj.height || (w / scale);
          var quality = 0.9;
          //生成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(that, 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);
        }
      },
      convertBase64UrlToBlob: function (urlData) {
        var bytes = window.atob(urlData.split(',')[1]);        //去掉url的头,并转换为byte
        //处理异常,将ascii码小于0的转换为大于0
        var ab = new ArrayBuffer(bytes.length);
        var ia = new Uint8Array(ab);
        for (var i = 0; i < bytes.length; i++) {
          ia[i] = bytes.charCodeAt(i);
        }
        return new Blob([ab], {type: 'image/png'});
      },
      deleteImg:function (n) {
        console.log(n);
        this.imgs.splice(n,1);
        this.subFile.splice(n,1);
        console.log(this.imgs)
        console.log(this.subFile)
      },

      subm:function () {
          var vm = this;
          var formData = new FormData();
          for(var k in this.subFile){ //文件数组
            formData.append('pPath',this.subFile[k]);
          }
          formData.append('type',this.addType);
          $.ajax({
            type:"post",
            url:"",
            data:formData,
            cache: false,
            contentType: false,
            processData:false,
            mimeType:"multipart/form-data",
            success:function(data){
              var a=JSON.parse(data);
              console.log(a);
            }});
      }
    },
  })
</script>

</html>

通过控制台可以打印出压缩前后的图片大小

最后分享一个基于vue的图片上传插件 vue-corppa 官网链接:https://zhanziyang.github.io/vue-croppa/#/quick-start很是好用。功能齐全,包括图片的压缩,裁剪,形状裁剪等

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章