在手機端使用拍照功能上傳圖片的功能的解決文案

bluetooth發表於2021-09-09

主要依賴了一個compress.js的檔案,檔案內容如下: 

```

/*
* Tencent is pleased to support the open source community by making WeUI.js available.
* 
* Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved.
* 
* Licensed under the MIT License (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
* 
*       
* 
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*//**
 * 檢查圖片是否有被壓扁,如果有,返回比率
 * ref to http://stackoverflow.com/questions/11929099/html5-canvas-drawimage-ratio-bug-ios
 */function detectVerticalSquash(img) {
    // 拍照在IOS7或以下的機型會出現照片被壓扁的bug
    var data;
    var ih = img.naturalHeight;
    var canvas = document.createElement('canvas');
    canvas.width = 1;
    canvas.height = ih;
    var ctx = canvas.getContext('2d');
    ctx.drawImage(img, 0, 0);
    try {
        data = ctx.getImageData(0, 0, 1, ih).data;
    } catch (err) {
        console.log('Cannot check verticalSquash: CORS?');
        return 1;
    }
    var sy = 0;
    var ey = ih;
    var py = ih;
    while (py > sy) {
        var alpha = data[(py - 1) * 4 + 3];
        if (alpha === 0) {
            ey = py;
        } else {
            sy = py;
        }
        py = (ey + sy) >> 1; // py = parseInt((ey + sy) / 2)
    }
    var ratio = (py / ih);
    return (ratio === 0) ? 1 : ratio;}/**
 * dataURI to blob, ref to 
 * @param dataURI
 */function dataURItoBuffer(dataURI){
    var byteString = atob(dataURI.split(',')[1]);
    var buffer = new ArrayBuffer(byteString.length);
    var view = new Uint8Array(buffer);
    for (var i = 0; i < byteString.length; i++) {
        view[i] = byteString.charCodeAt(i);
    }
    return buffer;}function dataURItoBlob(dataURI) {
    var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
    var buffer = dataURItoBuffer(dataURI);
    return new Blob([buffer], {type: mimeString});}/**
 * 獲取圖片的orientation
 * ref to 
 */function getOrientation(buffer){
    var view = new DataView(buffer);
    if (view.getUint16(0, false) != 0xFFD8) return -2;
    var length = view.byteLength, offset = 2;
    while (offset < length) {
        var marker = view.getUint16(offset, false);
        offset += 2;
        if (marker == 0xFFE1) {
            if (view.getUint32(offset += 2, false) != 0x45786966) return -1;
            var little = view.getUint16(offset += 6, false) == 0x4949;
            offset += view.getUint32(offset + 4, little);
            var tags = view.getUint16(offset, little);
            offset += 2;
            for (var i = 0; i < tags; i++)
                if (view.getUint16(offset + (i * 12), little) == 0x0112)
                    return view.getUint16(offset + (i * 12) + 8, little);
        }
        else if ((marker & 0xFF00) != 0xFF00) break;
        else offset += view.getUint16(offset, false);
    }
    return -1;}/**
 * 修正拍照時圖片的方向
 * ref to 
 */function orientationHelper(canvas, ctx, orientation) {
    const w = canvas.width, h = canvas.height;
    if(orientation > 4){
        canvas.width = h;
        canvas.height = w;
    }
    switch (orientation) {
        case 2:
            ctx.translate(w, 0);
            ctx.scale(-1, 1);
            break;
        case 3:
            ctx.translate(w, h);
            ctx.rotate(Math.PI);
            break;
        case 4:
            ctx.translate(0, h);
            ctx.scale(1, -1);
            break;
        case 5:
            ctx.rotate(0.5 * Math.PI);
            ctx.scale(1, -1);
            break;
        case 6:
            ctx.rotate(0.5 * Math.PI);
            ctx.translate(0, -h);
            break;
        case 7:
            ctx.rotate(0.5 * Math.PI);
            ctx.translate(w, -h);
            ctx.scale(-1, 1);
            break;
        case 8:
            ctx.rotate(-0.5 * Math.PI);
            ctx.translate(-w, 0);
            break;
    }}/**
 * 壓縮圖片
 */function compress(file, options, callback) {
    const reader = new FileReader();
    reader.onload = function (evt) {
        if(options.compress === false){
            // 不啟用壓縮 & base64上傳 的分支,不做任何處理,直接返回檔案的base64編碼
            file.base64 = evt.target.result;
            callback(file);
            return;
        }

        // 啟用壓縮的分支
        const img = new Image();
        img.onload = function () {
            const ratio = detectVerticalSquash(img);
            const orientation = getOrientation(dataURItoBuffer(img.src));
            const canvas = document.createElement('canvas');
            const ctx = canvas.getContext('2d');

            const maxW = options.compress.width;
            const maxH = options.compress.height;
            let w = img.width;
            let h = img.height;
            let dataURL;

            if(w < h && h > maxH){
                w = parseInt(maxH * img.width / img.height);
                h = maxH;
            }else if(w >= h && w > maxW){
                h = parseInt(maxW * img.height / img.width);
                w = maxW;
            }

            canvas.width = w;
            canvas.height = h;

            if(orientation > 0){
                orientationHelper(canvas, ctx, orientation);
            }
            ctx.drawImage(img, 0, 0, w, h / ratio);

            if(/image/jpeg/.test(file.type) || /image/jpg/.test(file.type)){
                dataURL = canvas.toDataURL('image/jpeg', options.compress.quality);
            }else{
                dataURL =  canvas.toDataURL(file.type);
            }

            if(options.type == 'file'){
                if(/;base64,null/.test(dataURL) || /;base64,$/.test(dataURL)){
                    // 壓縮出錯,以檔案方式上傳的,採用原檔案上傳
                    console.warn('Compress fail, dataURL is ' + dataURL + '. Next will use origin file to upload.');
                    callback(file);
                }else{
                    let blob = dataURItoBlob(dataURL);
                    blob.id = file.id;
                    blob.name = file.name;
                    blob.lastModified = file.lastModified;
                    blob.lastModifiedDate = file.lastModifiedDate;
                    callback(blob);
                }
            }else{
                if(/;base64,null/.test(dataURL) || /;base64,$/.test(dataURL)){
                    // 壓縮失敗,以base64上傳的,直接報錯不上傳
                    options.onError(file, new Error('Compress fail, dataURL is ' + dataURL + '.'));
                    callback();
                }else{
                    file.base64 = dataURL;
                    callback(file);
                }
            }
        };
        img.src = evt.target.result;
    };
    reader.readAsDataURL(file);}export default {
    compress};

```

檔案中有多種bug處理,如,拍照在IOS7或以下的機型會出現照片被壓扁的bug 、修正拍照時圖片的方向、壓縮圖片等問題。

具體使用也大概說明一下,下例子使用用的是vue-cli

1, 引入 js   注在靜態檔案目錄下,es6引入,,注意,匯出的是物件,,且用的是default匯出

import compress from '@/assets/js/compress.js'

2, 下面是個人的使用方式,

```

<template>
    <div class="course-picture input_file_wrapper flex-center icon-default-img">
        <input type="file" @change="handleFile" name="file" class="input_file">
        <span class="course-picture-con" :style="{'background-image':'url('+logoImg+')'}"></span>
    </div></template><script>import compress from '@/assets/js/exif/compress.js' 

export default {
    data(){
        return {
            logoImg : ''
        }
    },
    props : {
        logo : {
            type : String,
            default : ''
        },
    },
    watch :{
        logo(val){
            this.logoImg = val
        }
    },
    components : {
    },
    methods : {
        // 選擇修改圖片事件
        handleFile(e) {
            e.stopPropagation()
            let _this = this            
            let file = e.target.files[0];
            if (!/image/w+/.test(file.type)) {
                this.$toast('請選擇圖片')
                return
            }       
            compress.compress(file,{
                compress: {
                    width:520,
                    height:520
                }
            },function (data) {
                _this.logoImg = data.base64
                _this.$emit('change',_this.logoImg)
            })          
        },
    }
}   

</script><style scoped>.course-picture {height: 2.5rem;width: 2.5rem;}</style>


```

說到這兒,懂些vue的大概就知道了,上面是一個當成元件的封裝,還可以更完善,謝謝點贊。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/3402/viewspace-2826050/,如需轉載,請註明出處,否則將追究法律責任。

相關文章