天天看點

VUE中頭像裁剪上傳到伺服器

在項目遇到了如題所述的需求,很是折騰了一番。畢竟寫前端和移動端native是不一樣的!!現在進入正題,把dom部分擺出來:

<div>
        <vueCropper style="width: 90%;height: 260px;margin-left: 5%;margin-top: 15px"
                    ref="cropper"
                    :img="option.img"
                    :outputSize="option.outputSize"
                    :outputType="option.outputType"
                    :info="option.info"
                    :canScale="option.canScale"
                    :autoCrop="option.autoCrop"
                    :autoCropWidth="option.autoCropWidth"
                    :autoCropHeight="option.autoCropHeight"
                    :fixed="option.fixed"
                    :fixedNumber="option.fixedNumber"></vueCropper>
        <input style="margin-left: 20px;margin-top: 15px" id="file_input" type="file" capture="camera"/>
        <div class="btn" @click="compressImg()">上傳頭像</div>
    </div>
           

可以看到,在dom中我們使用了vueCropper這個元件。這個是今天的主角,圖檔裁剪控件。上github:

https://github.com/xyxiao001/vue-cropper

安裝:$:npm install vue-cropper -s

vue-cropper元件的屬性:

名稱 功能 預設值 可選值
img 裁剪圖檔的位址 url 位址 || base64 || blob
outputSize 裁剪生成圖檔的品質 1 0.1 - 1
outputType 裁剪生成圖檔的格式 jpg (jpg 需要傳入jpeg) jpeg || png || webp
info 裁剪框的大小資訊 true true || false
canScale 圖檔是否允許滾輪縮放 true true || false
autoCrop 是否預設生成截圖框 false true || false
autoCropWidth 預設生成截圖框寬度 容器的80% 0~max
autoCropHeight 預設生成截圖框高度 容器的80% 0~max
fixed 是否開啟截圖框寬高固定比例 true true | false
fixedNumber 截圖框的寬高比例 [1 : 1] [寬度 : 高度]
full 是否輸出原圖比例的截圖 false true | false
fixedBox 固定截圖框大小 不允許改變 false true | false
canMove 上傳圖檔是否可以移動 true true | false
canMoveBox 截圖框能否拖動 true true | false
original 上傳圖檔按照原始比例渲染 false true | false
centerBox 截圖框是否被限制在圖檔裡面 false true | false
high 是否按照裝置的dpr 輸出等比例圖檔 true true | false
infoTrue true 為展示真實輸出圖檔寬高 false 展示看到的截圖框寬高 false true | false
maxImgSize 限制圖檔最大寬度和高度 2000 0-max

内置方法通過this.$refs.cropper 調用:

this.$refs.cropper.startCrop() 開始截圖

this.$refs.cropper.stopCrop() 停止截圖

this.$refs.cropper.clearCrop() 清除截圖

this.$refs.cropper.changeScale() 修改圖檔大小 正數為變大 負數變小

this.$refs.cropper.getImgAxis() 擷取圖檔基于容器的坐标點

this.$refs.cropper.getCropAxis() 擷取截圖框基于容器的坐标點

this.$refs.cropper.goAutoCrop 自動生成截圖框函數

this.$refs.cropper.rotateRight() 向右邊旋轉90度

this.$refs.cropper.rotateLeft() 向左邊旋轉90度

圖檔加載的回調 imgLoad 傳回結果success, error

// 擷取截圖的base64 資料
this.$refs.cropper.getCropData((data) => {
  // do something
  console.log(data)  
})

// 擷取截圖的blob資料
this.$refs.cropper.getCropBlob((data) => {
  // do something
  console.log(data)  
})
           

好了,vue-cropper就介紹到這裡。詳細的可以去github裡面看。

現在我們看看 input type='file' 通過Input我們可以得到選擇的圖檔,直接放入vueCropper中來:

mounted(){
            let _self = this;
            $("input[type='file']").change(function(){
                Indicator.open();
                let file = this.files[0];
                if (window.FileReader) {
                    let reader = new FileReader();
                    reader.readAsDataURL(file);
                    //監聽檔案讀取結束後事件    
                    reader.onloadend = function (e) {
                        _self.option.img = e.target.result;
                        Indicator.close();
                    };
                }
            });
        },
           

這裡使用jquery來擷取選取的圖檔,而且要放在mounted()生命周期鈎子中。不能放入之前的生命的鈎子中。

現在調用裁剪方法:

compressImg:function () {
                let _self = this;
                this.$refs.cropper.startCrop();
                this.$refs.cropper.getCropData((data) => {
                    let file = _self.convertBase64UrlToBlob(data);
                    file.name = 'head.jpg';
                    _self.uploadAction(file);
                })
            },
           

這裡我們用得到的BASE64資料轉換成Blob資料,直接把Blob當做file來這裡,不要忘記設定file.name。不然上傳檔案的時候是會出錯的!!得到資料之後,就開始上傳了,調用你自己的ajax方法吧!這裡我們使用axios來做圖檔上傳:

uploadAction:function (file) {
                Indicator.open();
                let _self = this;
                let param = new FormData();  // 建立form對象
                param.append('pics', file, file.name);  // 通過append向form對象添加資料
                let config = {
                    headers: {'Content-Type': 'multipart/form-data'}
                };
                // 添加請求頭
                this.$axios.post('/appapi/user/i/up-headimg/pics', param, config)
                    .then(response => {
                      if(response.status == 200){
                          let data = response.data;
                          Indicator.close();
                         //出來伺服器的傳回資料
                      }
                    })
                    .catch(error=>{
                        Indicator.close();
                        console.log(error);
                    })
            },
           

完整代碼如下:

​
<template>
    <div>
        <vueCropper style="width: 90%;height: 260px;margin-left: 5%;margin-top: 15px"
                    ref="cropper"
                    :img="option.img"
                    :outputSize="option.outputSize"
                    :outputType="option.outputType"
                    :info="option.info"
                    :canScale="option.canScale"
                    :autoCrop="option.autoCrop"
                    :autoCropWidth="option.autoCropWidth"
                    :autoCropHeight="option.autoCropHeight"
                    :fixed="option.fixed"
                    :fixedNumber="option.fixedNumber"></vueCropper>
        <input style="margin-left: 20px;margin-top: 15px" id="file_input" type="file" capture="camera"/>
        <div class="btn" @click="compressImg()">上傳頭像</div>
        <span>{{status}}</span>
    </div>
</template>

<script>
    import { MessageBox, Indicator,Toast,Actionsheet  } from "mint-ui";
    import $ from 'jquery'
    import VueCropper from "vue-cropper"
    export default {
        name: "ImageUpload",
        components:{
            VueCropper,
            MessageBox,
            Indicator,
            Toast,

        },
        mounted(){
            let _self = this;
            $("input[type='file']").change(function(){
                Indicator.open();
                let file = this.files[0];
                if (window.FileReader) {
                    let reader = new FileReader();
                    reader.readAsDataURL(file);
                    //監聽檔案讀取結束後事件    
                    reader.onloadend = function (e) {
                        _self.option.img = e.target.result;
                        Indicator.close();
                    };
                }
            });
        },
        data(){
            return{
                option: {
                    img: "",                         //裁剪圖檔的位址
                    info: true,                      //裁剪框的大小資訊
                    outputSize: 0.7,                   // 裁剪生成圖檔的品質
                    outputType: 'jpeg',              //裁剪生成圖檔的格式
                    canScale: true,                 // 圖檔是否允許滾輪縮放
                    autoCrop: true,                  // 是否預設生成截圖框
                    autoCropWidth: 100,              // 預設生成截圖框寬度
                    autoCropHeight: 100,             // 預設生成截圖框高度
                    fixed: false,                    //是否開啟截圖框寬高固定比例
                    fixedNumber: [4, 4]              //截圖框的寬高比例
                },
                status:'',
                headImg:''
            }
        },
        methods:{
            showToast:function (msg) {
                Toast({
                    message: msg,
                    position: 'bottom',
                    duration: 2000
                })
            },
            compressImg:function () {
                let _self = this;
                this.$refs.cropper.startCrop();
                this.$refs.cropper.getCropData((data) => {
                    let file = _self.convertBase64UrlToBlob(data);
                    file.name = 'head.jpg';
                    _self.uploadAction(file);
                })
            },
            uploadAction:function (file) {
                Indicator.open();
                let _self = this;
                let param = new FormData();  // 建立form對象
                param.append('pics', file, file.name);  // 通過append向form對象添加資料
                let config = {
                    headers: {'Content-Type': 'multipart/form-data'}
                };
                // 添加請求頭
                this.$axios.post('/appapi/user/i/up-headimg/pics', param, config)
                    .then(response => {
                      if(response.status == 200){
                          let data = response.data;
                          Indicator.close();
                          //處理伺服器傳回資料
                      }
                    })
                    .catch(error=>{
                        Indicator.close();
                        console.log(error);
                    })
            },
            // 将base64的圖檔轉換為file檔案
            convertBase64UrlToBlob(urlData) {
                let bytes = window.atob(urlData.split(',')[1]);//去掉url的頭,并轉換為byte
                //處理異常,将ascii碼小于0的轉換為大于0
                let ab = new ArrayBuffer(bytes.length);
                let ia = new Uint8Array(ab);
                for (var i = 0; i < bytes.length; i++) {
                    ia[i] = bytes.charCodeAt(i);
                }
                return new Blob([ab], { type: 'image/jpeg' });
            },
           
        }
    }
</script>

<style scoped>
    .btn{
        width: 80%;
        height: 40px;
        margin-top: 50px;
        color: white;
        border-radius: 20px;
        background-color: #3377ff;
        display: flex;
        align-items: center;
        justify-content: center;
        margin-left: 10%;
    }
</style>

​