vue图片上传组件

1、正常上传

<template>
  <div class="upload-wrapper">
    <div v-if="!imgUrl" class="noneData">
      <div @click="openLocals">
        <img src="./imgs/icon_jh.png" alt="">
        <div>上传图片</div>
      </div>
      <form :id="fileName+'Form'" enctype="multipart/form-data" style="display:none;">
        <input ref="pathClear" type="file"  accept="image/*" :name="fileName" @change="fileChanged"/>
      </form>
    </div>
    <div v-else class="havaData">
      <img :src="imgUrl" alt="" @click="actImg">
    </div>
  </div>
</template>
<script>
import $ from 'jquery';
import { Indicator, Toast } from 'mint-ui';
import { webPostObject } from './ossUpload/index.js';
export default {
  data() {
    return {
    };
  },
  props: {
    // file控件名称
    fileName: {
      type: String,
      required: true
    },
    // 图片地址
    imgUrl: {
      type: String,
      default: '',
      required: true
    },
  },
  methods: {
    // 点击图片
    actImg() {
      console.log('点击图片');
    },
    // 删除图片
    removePackage() {
      let _self = this;
      _self.$emit('removeImg');
    },
    // 上传图片
    openLocals() {
      $(`input[type='file'][name='${this.fileName}']`).click();
    },
    fileChanged() {
      this.uploadImg();
    },
    uploadImg() {
      let _self = this;
      let fileElement = $(`input[type='file'][name='${_self.fileName}']`);
      let filename = fileElement.val();
      let ext = filename.substring(filename.lastIndexOf('.')).toLowerCase();
      if (ext !== '.jpg' && ext !== '.png' && ext !== '.jpeg' && ext !== '.bmp') {
        Toast({
          message: '仅支持.jpg、.png、.jpeg、.bmp的图片格式',
          position: 'bottom',
          duration: 2000
        });
        return false;
      }
      let file = fileElement[0].files[0];
      let imgSize = Math.ceil(file.size / 1024 / 1024);
      if (imgSize > 20) {
        Toast({
          message: '文件不能大于20MB,重新选择文件',
          position: 'bottom',
          duration: 2000
        });
        _self.$refs.pathClear.value = '';
        return false;
      }
      Indicator.open('图片正在上传中...');
      // 服务器上传
      // let formdata1 = new FormData();
      // formdata1.append('file', file, file.name);
      // let config = {
      //   headers: { 'Content-Type': 'multipart/form-data' }
      // };
      // axios.post(`${process.env.UPLOAD_HOST}`, formdata1, config).then((res) => {
      //   console.log(res);
      //   let data = res.data;
      //   Indicator.close();
      //   if (data) {
      //     _self.$emit('uploadSuccess', data[0]);
      //   }
      // }).catch(() => {
      //   Indicator.close();
      //   Toast({
      //     message: '图片上传失败',
      //     position: 'bottom',
      //     duration: 2000
      //   });
      // });
      // oss web直传
      webPostObject(file, file.name).then(data => {
        console.log('oss web直传', data);
        Indicator.close();
        _self.$emit('uploadSuccess', data);
      }).catch(() => {
        Indicator.close();
        Toast({
          message: '图片上传失败',
          position: 'bottom',
          duration: 2000
        });
      });
    },
  },
};
</script>
<style lang="less" scoped>
.upload-wrapper{
  height: 100%;
}
.noneData,.havaData{
  position: relative;
  height: 100%;
}
.noneData{
  background-color: #eeeeee;
  color: #999999;
  font-size: 12px;
  text-align: center;
  padding-top: 15px;
  img{
     26px;
    height: 26px;
  }
}
.havaData{
  img{
     100%;
    height: 100%;
  }
}
</style>

2、压缩图片上传

<template>
  <div class="upload-wrapper">
    <div v-if="!imgUrl" class="noneData">
      <div class="uploadImg" @click="openLocals">
      </div>
      <form :id="fileName+'Form'" enctype="multipart/form-data" style="display:none;">
        <input ref="pathClear" type="file"  accept="image/*" :name="fileName" @change="fileChanged"/>
      </form>
    </div>
    <div v-else class="havaData">
     <div class="havaDataimg">
        <div class="img_wap">
          <img :src="imgUrl" alt="" @click.stop="bigPicture">
        </div>
        <div class="remove" @click.stop="removePackage"></div>
     </div>
    </div>
    <div class="maxImg" @click="minImg" v-if="maxImg">
      <img :src="imgUrl" alt="">
    </div>
  </div>
</template>
<script>
import $ from 'jquery';
import { Indicator, Toast } from 'mint-ui';
import axios from 'axios';
export default {
  data() {
    return {
      maxImg: false,
      xhr: null,
      ot: null,
      oloaded: null,
    };
  },
  props: {
    // file控件名称
    fileName: {
      type: String,
      required: true
    },
    // 图片地址
    imgUrl: {
      type: String,
      default: '',
      required: true
    },
    // 图片地址
    isMaxIMg: {
      type: String,
      default: '',
      required: true
    },
  },
  methods: {
    // 查看大图
    bigPicture() {
      console.log(this.isMaxIMg);
      if (this.isMaxIMg === '0') {
        return false;
      }
      this.maxImg = true;
    },
    // 恢复小图
    minImg() {
      this.maxImg = false;
    },
    // 删除图片
    removePackage() {
      let _self = this;
      _self.$emit('removeImg');
    },
    // 上传图片
    openLocals() {
      $(`input[type='file'][name='${this.fileName}']`).click();
    },
    fileChanged() {
      this.UpladFile();
    },
    // 三个参数
    // file:一个是文件(类型是图片格式),
    // w:一个是文件压缩的后宽度,宽度越小,字节越小
    // objDiv:一个是容器或者回调函数
    // photoCompress()
    photoCompress(file, w, objDiv) {
      let that = this;
      let ready = new FileReader();
      // 开始读取指定的Blob对象或File对象中的内容. 当读取操作完成时,readyState属性的值会成为DONE,如果设置了onloadend事件处理程序,则调用之.同时,result属性中将包含一个data: URL格式的字符串以表示所读取文件的内容.
      ready.readAsDataURL(file);
      ready.onload = function() {
        let re = this.result;
        that.canvasDataURL(re, w, objDiv);
      };
    },
    canvasDataURL(path, obj, callback) {
      let img = new Image();
      img.src = path;
      img.onload = function() {
        let that = this;
        // 默认按比例压缩
        let w = that.width;
        let h = that.height;
        let scale = w / h;
        w = obj.width || w;
        h = obj.height || (w / scale);
        let quality = 0.7;  // 默认图片质量为0.7
        // 生成canvas
        let canvas = document.createElement('canvas');
        let ctx = canvas.getContext('2d');
        // 创建属性节点
        let anw = document.createAttribute('width');
        anw.nodeValue = w;
        let 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值越小,所绘制出的图像越模糊
        let base64 = canvas.toDataURL('image/jpeg', quality);
        // 回调函数返回base64的值
        callback(base64);
      };
    },
    /**
     * 将以base64的图片url数据转换为Blob
     * @param urlData
     *            用url方式表示的base64图片数据
     */
    convertBase64UrlToBlob(urlData) {
      let arr = urlData.split(',');
      let mime = arr[0].match(/:(.*?);/)[1];
      let bstr = atob(arr[1]);
      let n = bstr.length;
      let u8arr = new Uint8Array(n);
      while (n--) {
        u8arr[n] = bstr.charCodeAt(n);
      }
      return new Blob([u8arr], { type: mime });
    },
    // 上传文件方法
    UpladFile() {
      let that = this;
      let fileElement = $(`input[type='file'][name='${that.fileName}']`);
      let filename = fileElement.val();
      let ext = filename.substring(filename.lastIndexOf('.')).toLowerCase();
      if (ext !== '.jpg' && ext !== '.png' && ext !== '.jpeg' && ext !== '.bmp') {
        Toast({
          message: '仅支持.jpg、.png、.jpeg、.bmp的图片格式',
          position: 'bottom',
          duration: 2000
        });
        return false;
      }
      let fileObj = fileElement[0].files[0];
      let form = new FormData(); // FormData 对象
      if (fileObj.size / 1024 > 1025) { // 大于1M,进行压缩上传
        that.photoCompress(fileObj, {
          quality: 0.2
        }, function(base64Codes) {
          let bl = that.convertBase64UrlToBlob(base64Codes);
          form.append('file', bl, 'file_' + Date.parse(new Date()) + '.jpg'); // 文件对象
          let config = {
            headers: { 'Content-Type': 'multipart/form-data' }
          };
          Indicator.open('图片正在上传中...');
          axios.post(`${process.env.UPLOAD_HOST}`, form, config).then((res) => {
            console.log(res);
            let data = res.data;
            Indicator.close();
            if (data) {
              that.$emit('uploadSuccess', data[0]);
            }
          }).catch(() => {
            Indicator.close();
            Toast({
              message: '图片过大上传失败',
              position: 'bottom',
              duration: 2000
            });
          });
        });
      } else { // 小于等于1M 原图上传
        Indicator.open('图片正在上传中...');
        form.append('file', fileObj); // 文件对象
        axios.post(`${process.env.UPLOAD_HOST}`, form).then((res) => {
          console.log(res);
          let data = res.data;
          Indicator.close();
          if (data) {
            that.$emit('uploadSuccess', data[0]);
          }
        }).catch(() => {
          Indicator.close();
          Toast({
            message: '图片过大上传失败',
            position: 'bottom',
            duration: 2000
          });
        });
      }
    },
  }
};
</script>

<style lang="less" scoped>
.upload-wrapper{
  position: relative;
  height: 172px;
}
.noneData,.havaData{
  height: 100%;
  padding: 15px 16vw;
  position: relative;
  display: flex;
  justify-content: center;
  align-items: center;
  .uploadImg{
     102px;
    height: 102px;
    background-size: 100% 100%;
    background-image: url(./imgs/xh_pfgy_upload.png);
  }
  .havaDataimg{
    position: relative;
     100%;
    height: 100%;
    border: 1px solid #eeeeec;
    overflow: hidden;
    .img_wap{
       100%;
      height: 0px;
      padding-bottom: 56%;
      position: relative;
      img{
         100%;
        position: absolute;
        top: -30%;
        left: 0px;
      }
    }
    .remove{
      position: absolute;
      top: 0;
      right: 0;
       18px;
      height: 18px;
      background: url('./imgs/icon_close.png') rgba(255, 255, 255, 0.8) center center no-repeat;
      background-size: cover;
      border-radius: 50%;
    }
  }
}
// 大图效果
.maxImg{
  position: fixed;
  top: 0px;
  left: 0px;
  right: 0px;
  bottom: 0px;
  overflow: auto;
  background-color:rgba(0, 0, 0, 0.5);
  z-index: 100;
  padding: 20px;
  display: flex;
  justify-content: center;
  align-items: center;
  img{
    max- 100%;
    max-height: 100%;
  }
}
</style>

3、转为base64上传

<template>
  <div>
    <div style="padding:20px;">
      <!-- <div class="show">
        <div class="picture" :style="'backgroundImage:url('+headerImage+')'"></div>
      </div> -->
      <div class="havaDataimg">
         <div class="img_wap">
          <img  :src="headerImage"  alt="">
        </div>
        <div class="remove"></div>
      </div>
      <div style="margin-top:20px;">
        <input type="file" id="upload" accept="image/*" @change="upload">
        <label for="upload"></label>
      </div>
    </div>
  </div>
</template>
<script>
import Exif from 'exif-js';
import { Toast } from 'mint-ui';
export default {
  data() {
    return {
      headerImage: '',
      picValue: ''
    };
  },
  methods: {
    upload(e) {
      let files = e.target.files || e.dataTransfer.files;
      if (!files.length) return;
      this.picValue = files[0];
      let filename = this.picValue.name;
      let ext = filename.substring(filename.lastIndexOf('.')).toLowerCase();
      if (ext !== '.jpg' && ext !== '.png' && ext !== '.jpeg' && ext !== '.bmp') {
        Toast({
          message: '仅支持.jpg、.png、.jpeg、.bmp的图片格式',
          position: 'bottom',
          duration: 2000
        });
        return false;
      }
      this.imgPreview(this.picValue);
    },
    imgPreview(file) {
      let self = this;
      let Orientation;
      // 去获取拍照时的信息,解决拍出来的照片旋转问题
      Exif.getData(file, function() {
        Orientation = Exif.getTag(this, 'Orientation');
      });
      // 看支持不支持FileReader
      if (!file || !window.FileReader) return;

      if (/^image/.test(file.type)) {
        // 创建一个reader
        let reader = new FileReader();
        // 将图片2将转成 base64 格式
        reader.readAsDataURL(file);
        // 读取成功后的回调
        reader.onloadend = function () {
          let result = this.result;
          let img = new Image();
          img.src = result;
          // 判断图片是否大于100K,是就直接上传,反之压缩图片
          if (this.result.length <= (100 * 1024)) {
            self.headerImage = this.result;
            self.postImg();
          } else {
            img.onload = function () {
              let data = self.compress(img, Orientation);
              self.headerImage = data;
              self.postImg();
            };
          }
        };
      }
    },
    postImg() {
      // 这里写接口
    },
    rotateImg(img, direction, canvas) {
      // 最小与最大旋转方向,图片旋转4次后回到原方向
      const min_step = 0;
      const max_step = 3;
      if (img == null) return;
      // img的高度和宽度不能在img元素隐藏后获取,否则会出错
      let height = img.height;
      let width = img.width;
      let step = 2;
      if (step == null) {
        step = min_step;
      }
      if (direction === 'right') {
        step++;
        // 旋转到原位置,即超过最大值
        step > max_step && (step = min_step);
      } else {
        step--;
        step < min_step && (step = max_step);
      }
      // 旋转角度以弧度值为参数
      let degree = step * 90 * Math.PI / 180;
      let ctx = canvas.getContext('2d');
      switch (step) {
        case 0:
          canvas.width = width;
          canvas.height = height;
          ctx.drawImage(img, 0, 0);
          break;
        case 1:
          canvas.width = height;
          canvas.height = width;
          ctx.rotate(degree);
          ctx.drawImage(img, 0, -height);
          break;
        case 2:
          canvas.width = width;
          canvas.height = height;
          ctx.rotate(degree);
          ctx.drawImage(img, -width, -height);
          break;
        case 3:
          canvas.width = height;
          canvas.height = width;
          ctx.rotate(degree);
          ctx.drawImage(img, -width, 0);
          break;
      }
    },
    compress(img, Orientation) {
      let canvas = document.createElement('canvas');
      let ctx = canvas.getContext('2d');
      // 瓦片canvas
      let tCanvas = document.createElement('canvas');
      let tctx = tCanvas.getContext('2d');
      let initSize = img.src.length;
      let width = img.width;
      let height = img.height;
      // 如果图片大于四百万像素,计算压缩比并将大小压至400万以下
      let ratio;
      if ((ratio = width * height / 4000000) > 1) {
        console.log('大于400万像素');
        ratio = Math.sqrt(ratio);
        width /= ratio;
        height /= ratio;
      } else {
        ratio = 1;
      }
      canvas.width = width;
      canvas.height = height;
      //        铺底色
      ctx.fillStyle = '#fff';
      ctx.fillRect(0, 0, canvas.width, canvas.height);
      // 如果图片像素大于100万则使用瓦片绘制
      let count;
      if ((count = width * height / 1000000) > 1) {
        console.log('超过100W像素');
        count = ~~(Math.sqrt(count) + 1); // 计算要分成多少块瓦片
        //            计算每块瓦片的宽和高
        let nw = ~~(width / count);
        let nh = ~~(height / count);
        tCanvas.width = nw;
        tCanvas.height = nh;
        for (let i = 0; i < count; i++) {
          for (let j = 0; j < count; j++) {
            tctx.drawImage(img, i * nw * ratio, j * nh * ratio, nw * ratio, nh * ratio, 0, 0, nw, nh);
            ctx.drawImage(tCanvas, i * nw, j * nh, nw, nh);
          }
        }
      } else {
        ctx.drawImage(img, 0, 0, width, height);
      }
      // 修复ios上传图片的时候 被旋转的问题
      if (Orientation !== '' && Orientation !== 1) {
        switch (Orientation) {
          case 6:// 需要顺时针(向左)90度旋转
            this.rotateImg(img, 'left', canvas);
            break;
          case 8:// 需要逆时针(向右)90度旋转
            this.rotateImg(img, 'right', canvas);
            break;
          case 3:// 需要180度旋转
            this.rotateImg(img, 'right', canvas);// 转两次
            this.rotateImg(img, 'right', canvas);
            break;
        }
      }
      // 进行最小压缩
      let ndata = canvas.toDataURL('image/jpeg', 0.7);
      console.log('压缩前:' + initSize);
      console.log('压缩后:' + ndata.length);
      console.log('压缩率:' + ~~(100 * (initSize - ndata.length) / initSize) + '%');
      tCanvas.width = tCanvas.height = canvas.width = canvas.height = 0;
      return ndata;
    },
  }
};
</script>

<style lang="less" scoped>
*{
  margin: 0;
  padding: 0;
}
/* .show {
   100px;
  height: 100px;
  overflow: hidden;
  position: relative;
  border-radius: 50%;
  border: 1px solid #d5d5d5;
}
.picture {
   100%;
  height: 100%;
  overflow: hidden;
  background-position: center center;
  background-repeat: no-repeat;
  background-size: cover;
} */
.havaDataimg{
  position: relative;
   100%;
  height: 100%;
  border: 1px solid #eeeeec;
  overflow: hidden;
  .img_wap{
     100%;
    height: 0px;
    position: relative;
    padding-bottom: 56.25%;
    img{
       100%;
      position: absolute;
      top: -30%;
      left: 0px;
    }
  }
}
</style>

 4引入组件使用

import uploadImg from '@/components/upload/prescriptionUploadImg';


components: {
    'uploadImg': uploadImg,
  },





<uploadImg :imgUrl="pic_url" :fileName="'Image'" @uploadSuccess="addImgUrl" @removeImg="removeImgUrl"></uploadImg>

  

原文地址:https://www.cnblogs.com/yangzhenhong/p/11023586.html