第一:需要安装依赖包

封装上传图片:

<template>
  <div id="demo">
    <!-- 遮罩层 -->
  <div class="father" v-show="panel">
    <div class="container">
      <div id="cropper" style="margin-top:1%;margin-left: 1%;margin-right: 1%;height: 82%;margin-bottom: 1%">
        <img :id="params.cropperId" class="images" :src="url" alt="Picture">
      </div>
      <div>
        <div style="float: right;margin-right: 1%">
          <el-button icon="el-icon-zoom-out" @click="zoom(-1)" size="mini" circle></el-button>
          <el-button icon="el-icon-zoom-in" @click="zoom(1)" size="mini" circle></el-button>
          <el-button icon="el-icon-refresh" @click="rotate" size="mini" circle></el-button>
          <el-button type="success" icon="el-icon-check" @click="crop" size="mini" circle></el-button>
          <el-button type="danger" icon="el-icon-close" @click="panel=false" size="mini" circle></el-button>
        </div>
      </div>
    </div>
  </div>
    <div >
      <div >
        <input type="file" :id="params.id" accept="image" @change="change" style="display:none;">
        <div v-if="!params.slotFlag" class="show" v-on:mouseover="addClassload"  v-on:mouseout="removeClassload" @click="upload" :style="'backgroundImage:url(../assets/mao.jpg);border: 1px dashed '+color">
          <i class="el-icon-plus i" :style="'color: '+color"></i>
        </div>
        <div v-else @click="upload">
          <slot name="uploadBtn"></slot>
        </div>
      </div>
    </div>
  </div>
 
</template>
 
 
<script>
  // import {test} from '@/api/psych'
  // import { uploadImg } from '@/api/common'
  import Cropper from 'cropperjs'
  export default {
    components: {
 
    },
    props:{
      params:{
        id:'change',
        cropperId:"croppers",
        slotFlag:false,//显示自定义按钮
        name:'uploadImg'
      }
    },
    data () {
      return {
        headerImage: '',
        picValue:'',
        cropper:'',
        croppable:false,
        panel:false,
        url:'',
        color:"#d9d9d9",
        current:0,
 
      }
    },
    mounted () {
      //初始化这个裁剪框
      var self = this;
      let id = this.params.cropperId || 'cropper'+Math.random()
      var image = document.getElementById(id);
      this.cropper = new Cropper(image, {
        aspectRatio: NaN, //Number,croper box的宽高比,可以为裁剪设置固定的宽高比,值为NaN时,可自由裁剪。可以使用Shift键来切换或者固定宽高比。
        viewMode: 1, //Number,默认值0,可选值0,1,2,3 mode为0的情况下,crop box部分可以超出canvans的范围,mode为1,2,3时crop box被限制在canvas范围之内,mode为2,3时会将canvas限制在container之内。
        zoomOnWheel:false,//是否允许通过鼠标滚轮来缩放图片
        background:true,//是否在容器上显示网格背景
        rotatable:true,//是否允许旋转图片
        ready: function () {
          self.croppable = true;
        }
      });
      // console.log(133)
      // console.log(this.params.logo)
      this.headerImage = this.params.logo
    },
    methods: {
      addClassload(){
        this.color="#1b95e0"
      },
      removeClassload(){
        this.color="#d9d9d9"
      },
      //点击按钮自动执行选择文件事件
      upload(){
        this.url='',
        this.current=0;
        let id = this.params.id
        document.getElementById(id).value=null;
        document.getElementById(id).click();
      },
      //旋转
      rotate(){
        //alert(this.cropper.image)
        this.current = (this.current+90)%360;
        this.cropper.rotate(this.current);
      },
      //缩放
      zoom(num){
        num = num || 1;
        this.cropper.zoom(num);
      },
 
      getObjectURL (file) {
        var url = null ;
        if (window.createObjectURL!=undefined) { // basic
          url = window.createObjectURL(file) ;
        } else if (window.URL!=undefined) { // mozilla(firefox)
          url = window.URL.createObjectURL(file) ;
        } else if (window.webkitURL!=undefined) { // webkit or chrome
          url = window.webkitURL.createObjectURL(file) ;
        }
        // console.log(file)
        return url ;
      },
      change (e) {
        let files = e.target.files || e.dataTransfer.files;
        if (!files.length) return;
        this.panel = true;
        this.picValue = files[0];
        this.url = this.getObjectURL(this.picValue);
        //每次替换图片要重新得到新的url
        if(this.cropper){
          this.cropper.replace(this.url);
        }
        
        // console.log(files)
        this.panel = true;
      },
      crop () {
        this.panel = false;
        var croppedCanvas;
        var roundedCanvas;
 
        if (!this.croppable) {
          return;
        }
        // Crop
        croppedCanvas = this.cropper.getCroppedCanvas();
        // console.log(this.cropper)
        // Round
        /*截取圆形
        roundedCanvas = this.getRoundedCanvas(croppedCanvas);
        this.headerImage = roundedCanvas.toDataURL();
        */
        //方形
        this.headerImage = croppedCanvas.toDataURL();
 
        var gettype=Object.prototype.toString
        this.postImg()
 
      },
      dataURLtoFile (dataurl, filename = 'file') {
        let arr = dataurl.split(',')
        let mime = arr[0].match(/:(.*?);/)[1]
        let suffix = mime.split('/')[1]
        let bstr = atob(arr[1])
        let n = bstr.length
        let u8arr = new Uint8Array(n)
        while (n--) {
          u8arr[n] = bstr.charCodeAt(n)
        }
        return new File([u8arr], `${filename}.${suffix}`, {type: mime})
      },
     /* // 截取圆形
      getRoundedCanvas (sourceCanvas) {
        var canvas = document.createElement('canvas');
        var context = canvas.getContext('2d');
        var width = sourceCanvas.width;
        var height = sourceCanvas.height;
        canvas.width = width;
        canvas.height = height;
        context.imageSmoothingEnabled = true;
        context.drawImage(sourceCanvas, 0, 0, width, height);
        context.globalCompositeOperation = 'destination-in';
        context.beginPath();
        context.arc(width / 2, height / 2, Math.min(width, height) / 2, 0, 2 * Math.PI, true);
        context.fill();
        return canvas;
      },*/

      // base64转blob
      dataURLtoBlob(dataurl) { 
          var arr = dataurl.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 });
      },
      //ArrayBuffer ---> Buffer
      toBuffer(ab) {
          var buf = new Buffer(ab.byteLength);
          var view = new Uint8Array(ab);
          for (var i = 0; i < buf.length; ++i) {
              buf[i] = view[i];
          }
          return buf;
      },

      async postImg () {
        //这边写图片的上传
        var formData = new FormData();
        formData.append("avatar",this.dataURLtoFile(this.headerImage));
        // console.log(this.headerImage)
        const path =  await this.imgUpload(this.headerImage); // imgUpload 方法写在 mixin 里,下文会提到
        // console.log('path')
        // console.log(path)
        this.params.name = this.params.name || 'uploadImg'
        this.$emit('changeImg',path,this.params.name)
        return false;
        const blob = this.dataURLtoBlob(this.headerImage);
        // blob转arrayBuffer
        const reader = new FileReader();
        reader.readAsArrayBuffer(blob);
        reader.onload = event => {
          // arrayBuffer转Buffer
          const buffer = this.toBuffer(event.target.result);
          console.log(buffer)
        }
        // uploadImg(formData).then(response => {
        //   if(response.data.code == 0){
        //       //  alert("成功");
        //       this.params.logo = response.data.data
        //        this.$emit('changeImg',response.data.data)
        //   }else{
        //     //   this.$toast(res.data.msg)
        //     }
 
        // }).catch((res) => {
        //     //   this.$toast('网路异常')
        //   })
 
      }
    }
  }
 
</script>
 
<style>
  *{
    margin: 0;
    padding: 0;
  }
  .father{
    background-color: rgba(0,0,0,0.5);
    position: fixed;
    left:0px;
    top:0px;
    width:100%;
    height:100%;
    z-index: 33;
  }
  .i{
    font-size: 28px;
    color: #8c939d;
    width: 60px;
    height: 60px;
    line-height: 60px;
    text-align: center;
  }
  #demo .show {
    border: 1px dashed #d9d9d9;
    border-radius: 10%;
    width: 60px;
    height: 60px;
    cursor: pointer;
    position: relative;
    overflow: hidden;
    display: inline-block;
    text-align: center;
    outline: none;
    background-position: center center;
    background-repeat: no-repeat;
    background-size: cover;
  }
  #demo .container {
    z-index: 99;
    height: 400px;
    width: 50%;
    position: fixed;
    /*padding-top: 60px;*/
    left: 25%;
    top: 25%;
 
    background-color: white;
  }
  #demo .images {
    max-width: 100%;
 
  }
 /* .cropper-view-box,.cropper-face {
    border-radius: 100%; 圆形截图设置
  }*/
  /*!
   * Cropper.js v1.0.0-rc
   * https://github.com/fengyuanchen/cropperjs
   *
   * Copyright (c) 2017 Fengyuan Chen
   * Released under the MIT license
   *
   * Date: 2017-03-25T12:02:21.062Z
   */
  .cropper-container {
    font-size: 0;
    line-height: 0;
    position: relative;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
    direction: ltr;
    -ms-touch-action: none;
    touch-action: none
  }
 
  .cropper-container img {
    /* Avoid margin top issue (Occur only when margin-top <= -height) */
    display: block;
    min-width: 0 !important;
    max-width: none !important;
    min-height: 0 !important;
    max-height: none !important;
    width: 100%;
    height: 100%;
    image-orientation: 0deg
  }
  .cropper-wrap-box,
  .cropper-canvas,
  .cropper-drag-box,
  .cropper-crop-box,
  .cropper-modal {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
  }
  .cropper-wrap-box {
    overflow: hidden;
  }
  .cropper-drag-box {
    opacity: 0;
    background-color: #fff;
  }
  .cropper-modal {
    opacity: .5;
    background-color: #000;
  }
  .cropper-view-box {
    display: block;
    overflow: hidden;
    width: 100%;
    height: 100%;
    outline: 1px solid #39f;
    outline-color: rgba(51, 153, 255, 0.75);
  }
 
  .cropper-dashed {
    position: absolute;
    display: block;
    opacity: .5;
    border: 0 dashed #eee
  }
  .cropper-dashed.dashed-h {
    top: 33.33333%;
    left: 0;
    width: 100%;
    height: 33.33333%;
    border-top-width: 1px;
    border-bottom-width: 1px
  }
  .cropper-dashed.dashed-v {
    top: 0;
    left: 33.33333%;
    width: 33.33333%;
    height: 100%;
    border-right-width: 1px;
    border-left-width: 1px
  }
  .cropper-center {
    position: absolute;
    top: 50%;
    left: 50%;
    display: block;
    width: 0;
    height: 0;
    opacity: .75
  }
  .cropper-center:before,
  .cropper-center:after {
    position: absolute;
    display: block;
    content: ' ';
    background-color: #eee
  }
  .cropper-center:before {
    top: 0;
    left: -3px;
    width: 7px;
    height: 1px
  }
  .cropper-center:after {
    top: -3px;
    left: 0;
    width: 1px;
    height: 7px
  }
  .cropper-face,
  .cropper-line,
  .cropper-point {
    position: absolute;
    display: block;
    width: 100%;
    height: 100%;
    opacity: .1;
  }
  .cropper-face {
    top: 0;
    left: 0;
    background-color: #fff;
  }
  .cropper-line {
    background-color: #39f
  }
  .cropper-line.line-e {
    top: 0;
    right: -3px;
    width: 5px;
    cursor: e-resize
  }
  .cropper-line.line-n {
    top: -3px;
    left: 0;
    height: 5px;
    cursor: n-resize
  }
  .cropper-line.line-w {
    top: 0;
    left: -3px;
    width: 5px;
    cursor: w-resize
  }
  .cropper-line.line-s {
    bottom: -3px;
    left: 0;
    height: 5px;
    cursor: s-resize
  }
  .cropper-point {
    width: 5px;
    height: 5px;
 
    opacity: .75;
    background-color: #39f
  }
  .cropper-point.point-e {
    top: 50%;
    right: -3px;
    margin-top: -3px;
    cursor: e-resize
  }
  .cropper-point.point-n {
    top: -3px;
    left: 50%;
    margin-left: -3px;
    cursor: n-resize
  }
  .cropper-point.point-w {
    top: 50%;
    left: -3px;
    margin-top: -3px;
    cursor: w-resize
  }
  .cropper-point.point-s {
    bottom: -3px;
    left: 50%;
    margin-left: -3px;
    cursor: s-resize
  }
  .cropper-point.point-ne {
    top: -3px;
    right: -3px;
    cursor: ne-resize
  }
  .cropper-point.point-nw {
    top: -3px;
    left: -3px;
    cursor: nw-resize
  }
  .cropper-point.point-sw {
    bottom: -3px;
    left: -3px;
    cursor: sw-resize
  }
  .cropper-point.point-se {
    right: -3px;
    bottom: -3px;
    width: 20px;
    height: 20px;
    cursor: se-resize;
    opacity: 1
  }
  @media (min-width: 768px) {
    .cropper-point.point-se {
      width: 15px;
      height: 15px
    }
  }
  @media (min-width: 992px) {
    .cropper-point.point-se {
      width: 10px;
      height: 10px
    }
  }
  @media (min-width: 1200px) {
    .cropper-point.point-se {
      width: 5px;
      height: 5px;
      opacity: .75
    }
  }
  .cropper-point.point-se:before {
    position: absolute;
    right: -50%;
    bottom: -50%;
    display: block;
    width: 200%;
    height: 200%;
    content: ' ';
    opacity: 0;
    background-color: #39f
  }
  .cropper-invisible {
    opacity: 0;
  }
  .cropper-bg {
    background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC');
  }
  .cropper-hide {
    position: absolute;
    display: block;
    width: 0;
    height: 0;
  }
  .cropper-hidden {
    display: none !important;
  }
  .cropper-move {
    cursor: move;
  }
  .cropper-crop {
    cursor: crosshair;
  }
  .cropper-disabled .cropper-drag-box,
  .cropper-disabled .cropper-face,
  .cropper-disabled .cropper-line,
  .cropper-disabled .cropper-point {
    cursor: not-allowed;
  }
</style>
View Code

备注:需要动态设置不同id以防止同一个页面多次引入组件;(具体id在props有);

第二:设置阿里云部分

在utils文件夹下 创建upload.js

const OSS = require('ali-oss');
const moment = require('moment');

const env = process.env.NODE_ENV;

let expirationTime = null; // STS token 过期时间
let client = null; // OSS Client 实例

const bucket = {
  development: 'brinvest',
  dev: 'brinvest',
  pre: 'filespre',
  beta: 'filespro',
  production: 'filespro'
};

// 初始化 oss client
export function initOssClient(accessKeyId, accessKeySecret, stsToken, expiration, bucketName) {
  client = new OSS({
    accessKeyId,
    accessKeySecret,
    stsToken,
    // region: '/',
    region: 'oss-cn-beijing',
    bucket: bucketName
    // bucket: bucket[`${env}`]
  });
  expirationTime = expiration;
  return client;
}

// 检查 oss 实例以及过期时间
export function checkOssClient() {
  const current = moment();
  return moment(expirationTime).diff(current) < 0 ? null : client;
}

// 用于 sts token 失效、用户登出时注销 oss client
export function destroyOssClient() {
  client = null;
}
View Code

在 mixins文件夹下 创建upload.js

import { checkOssClient, initOssClient } from '../utils/upload';
import { uploadOssImg } from '../api/common'
// import { resolve } from 'core-js/fn/promise';
const uploadMixin = {
  methods: {
    // 图片上传至 oss
    async imgUpload(opt) {
        // console.log(opt)
      // if (opt.file.size > 1024 * 1024) {
      //   this.$message.warning(`请上传小于1MB的图片`);
      //   return;
      // }
      // 获取文件后缀
      // const tmp = opt.file.name.split('.');
      // const extname = tmp.pop();
      // const extList = ['jpg', 'jpeg', 'png', 'gif'];
      // // 校验文件类型
      // const isValid = extList.includes(extname);
      // if (!isValid) {
      //   this.$message.warning(`只支持上传 jpg、jpeg、png、gif 格式的图片`);
      //   return;
      // }

      // 检查是否已有 Oss Client
      // let client = checkOssClient();
      let client = null;
      let getTokenRes = {}
      if (client === null) {
        try {
        //   const res = await this.$http.get('/api/oss/getAccessToken', {});
          const res = await uploadOssImg().then((resout) => {
              return resout
          })
          // console.log(res.data )
          if (res.code == 0) {
            const credentials = res.data; //fileName requestId
            getTokenRes = { ...res.data }
            client = initOssClient(
              credentials.accessKeyId,
              credentials.accessKeySecret,
              credentials.securityToken,
              credentials.expiration,
              credentials.bucketName
            );
          }
        } catch (error) {
          this.$message.error(`${error}`);
          return;
        }
      }
      // 生产随机文件名
      const randomName = Array(32)
        .fill(null)
        .map(() => Math.round(Math.random() * 16).toString(16))
        .join('');
      let path = getTokenRes.fileName +'.jpg' //brinvest/mZy3zteXmJGTn2q4zI00nZq0ltG2mJGTmJyXy2jHmwe2oda20mJeZmdCWnJqZmW
    //   path = 'brinvest/mZy3zteXmJGTn2q4zI00nZq0ltG2mJGTmJyXy2jHmwe2oda20mJeZmdCWnJqZmW'
      let url;
      // return path;
      // try {
        // 使用 multipartUpload 正式上传到 oss
        // console.log(path)
        // console.log(client)
        // const PromiseMsg = new Promise((resolve , reject) => {
        //   const blob = this.dataURLtoBlob(opt);
        //   // blob转arrayBuffer
        //   const reader = new FileReader();
        //   reader.readAsArrayBuffer(blob);
        //   reader.onload = event => {
        //     // arrayBuffer转Buffer
        //     const buffer = this.toBuffer(event.target.result);
        //     // console.log(buffer)
        //     //命名规则 以后缀为文件目录  md5为文件名  可自定义
        //     //${this.suffix}/${this.pw}.${this.suffix}
        //     client
        //       .put(path, buffer)
        //       .then(res => {
        //         // console.log(res);
        //           url = 'http://brinvest.bridata.com/' + path;
        //           // resolve(url)
        //           return url;
        //       })
        //       .catch(err => {
        //         console.log(err);
        //       });
        //     console.log('multipartUpload res')
        //     // console.log(result)
        //     // 去除 oss 分片上传后返回所带的查询参数,否则访问会 403
        //     // const ossPath = res.requestUrls[0]; 
        //     // // 替换协议,统一使用 'https://',否则 Android 无法显示图片
        //     // url = ossPath.replace('http://', 'https://');
        //     console.log('minix url111')
        //     // console.log(result)
        //   }
        // })
        // PromiseMsg.then((res) => { 
        //   console.log(res)
        //   return res
        // })
          const blob = this.dataURLtoBlob(opt);
          // blob转arrayBuffer
          const reader = new FileReader();
          reader.readAsArrayBuffer(blob);
          let res;      
          // reader.onload = (event) => {
            // arrayBuffer转Buffer
            // console.log('p')
            // const buffer = this.toBuffer(event.target.result);
            res = await  client.multipartUpload(path, blob, {
              //   progress: async function(p) {
              //     console.log(p)
              //     // progress is generator
              //     let e = {};
              //     e.percent = p * 100;
              //     // 上传进度条,el-upload 组件自带的方法
              //     // opt.onProgress(e);
              //   },
              });
              // console.log(res);
              if(res.res){
                url = 'http://bri.com/' + path;
              }
              return url;
          // }
        console.log('PromiseMsg');
        console.log(res);
      // } catch (error) {
      //   console.log('error')
      //   console.log(error)
      //   this.$message.error(`${error}`);
      // }
      console.log('minix url222')
      console.log(url)
      // resolve(url)
    },
    // base64转blob
    dataURLtoBlob(dataurl) { 
      var arr = dataurl.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 });
    },
    //ArrayBuffer ---> Buffer
    toBuffer(ab) {
        var buf = new Buffer(ab.byteLength);
        var view = new Uint8Array(ab);
        for (var i = 0; i < buf.length; ++i) {
            buf[i] = view[i];
        }
        return buf;
    },
  }
};

export default uploadMixin;
View Code

在main.js中引用

import uploadjs from '@/mixins/upload'
Vue.mixin(uploadjs)

备注:根据后台提供接口获取token等信息创建client OSS Client 实例 根据返回值获取对应图片信息;

 

posted on 2021-05-28 11:31  刘世涛6192  阅读(341)  评论(0编辑  收藏  举报