전방 이미지 압축, 방향 수정, 미리 보기, 플러그인 업로드 방법

전언
오늘 우리는 어떻게 그림의 압축, 방향 수정 플러그인을 만드는지 설명하고, 어떻게 업로드하고 미리 보기하는지 설명할 것이다.
왜 그림의 압축과 방향 교정에 중점을 두었습니까?
여러분들이 프로젝트를 하는 과정에서 자주 사진을 백엔드에 올리는 것을 만날 수 있으나 사진이 너무 커서 사진을 압축 처리해야 한다고 믿습니다.특히 모바일 기기에서 핸드폰으로 찍은 사진이 보편적으로 너무 커서 우리는 가끔 사진 한 장을 올려야 하기 때문에 아주 작으면 충분하다.또 일부 휴대전화에서 찍은 사진은 방향 각도 문제가 존재하기 때문에 사진 각도를 바로잡아야 한다.
많은 학우들이 대부분 다른 사람이 쓴 그림으로 압축해서 플러그인을 올린다.우리의 수요에 맞추어 이 플러그인들은 때때로 우리의 가장 이상적인 효과에 도달하지 못하고 자기가 쓰지도 못하고 쓸 줄도 모르니 매우 골치 아프다.오늘은 깊이 있게 분석하고 설명하여 여러분에게 자신의 이미지 압축, 방향 교정 플러그인을 작성하고 압축된 이미지 데이터를 미리 보고 업로드하는 것을 가르쳐 드리겠습니다.
글에서 사용된 H5의api와 EXIF.js 등 지식점을 모르면 문장 말미의 결어 중의 지식점 자료를 먼저 읽으세요.
실현 원리
그림을 압축하고 파일 리더, canvas,formdata 세 개의 h5api와 플러그인 EXIF를 업로드합니다.js.논리는 결코 어렵지 않다.전체 프로세스는 다음과 같습니다.
 (1) 사용자가 input file로 그림을 올릴 때 filereader로 사용자가 올린 그림 데이터를 읽기(base64 형식)
 (2) 그림 데이터를img 대상에 전송한 다음,img를canvas에 그려서EXIF를 사용합니다.js에서 그림 방향을 수정하고 canvas를 호출합니다.toDataURL은 그림을 압축하여 압축된base64 형식의 그림 데이터를 가져와 2진법으로 변환합니다
 (3) 압축된 그림의 바이너리 데이터를 가져와 미리 봅니다.
 (4) 압축된 이미지 바이너리 데이터를formdata에 끼워넣고 XmlHttpRequest를 통해formdata를 제출합니다
  이렇게 네 걸음으로 그림의 압축, 방향 수정, 미리 보기와 업로드를 완성했다.
플러그인 디자인 사고
실제 프로젝트에서 서로 다른 개발 프레임워크(vue.js/JQ/react.js/angular.js/anu.js 등)를 사용할 수 있음을 감안하면 이미지 미리보기의 UI 스타일도 다르고 이미지 데이터 업로드 방법도 다를 수 있습니다.그림 압축과 방향 교정 이 두 블록의 논리적 다변성이 비교적 낮기 때문에 우리는 그림 압축과 방향 교정을 추출하여 하나의 플러그인 라이브러리로 봉인한다.
【1】 이미지 데이터 획득
먼저 이미지 데이터, 즉 input file의change 이벤트를 감청한 다음에 업로드된 파일의 대상을 압축하는 files를 가져와 files를 [사진 압축, 방향 수정 플러그인]에 전송하여 처리한다.
이때 모든 사람의 요구에 따라 압축되지 않은 그림도 미리 볼 수 있다.
//      input onchange  ,    ,      ,          
filechooser.onchange = function () {
    var fileList = this.files;
    
    //        
    var files = Array.prototype.slice.call(fileList);
    files.forEach(function (file, i) {
        var reader = new FileReader();
        reader.onload = function () {
            var li = document.createElement("li")
            li.style.backgroundImage = 'url('+this.result+')';
            document.querySelector('.img_list').appendChild(li)
        }
        reader.readAsDataURL(file);
    });

    //      ,getCompressiveFileList            
    //           、         ,              ~_~ ↓↓↓
    var process = window.lzImgProcess();
    process(fileList, getCompressiveFileList);
}

【2】 이미지 압축, 방향 수정 플러그인 실현
위에서 그림 데이터를 가져오면 프로세스를 통해 그림을 압축하는 방법을 할 수 있습니다.압축된 그림도 canvas에 직접 그림을 그려서 toDataURL을 호출하면 되는 것이 아니다.
  IOS에서 canvas가 그림을 그리는 데는 두 가지 제한이 있습니다.
  우선 그림의 크기입니다. 만약에 그림의 크기가 2백만 화소를 초과하면 그림도 canvas에 그릴 수 없습니다.drawImage를 호출할 때 오류가 발생하지 않지만 toDataURL로 그림 데이터를 가져올 때 빈 그림 데이터를 얻습니다.
게다가 캔버스의 크기에 제한이 있다. 만약에 캔버스의 크기가 약 5백만 화소(즉 너비와 고승적)보다 크면 그림을 그릴 수 없을 뿐만 아니라 다른 어떤 것도 그릴 수 없다.
  은 상기 두 가지 제한에 대응해야 한다. 나는 그림의 폭, 고도의 압축을 1000px 이내로 제어하면 그림이 최대 2백만 화소를 넘지 않게 된다.전단 개발에서 1000px*1000px는 기본적으로 대부분의 수요를 만족시킬 수 있다.물론 더 완벽한 기와식 그리는 방법도 있다. 우리는 기와식 그리는 방법을 말한다.
이렇게 되면 IOS의 두 가지 제한이 해결됩니다.
 위에서 말한 제한 외에 두 개의 구덩이가 있다. 하나는 canvas의 toDataURL은 jpg만 압축할 수 있는 것이다( Tip ). 사용자가 올린 그림이 png이면 jpg로 전환해야 한다. 즉, 통일적으로 canvas를 사용해야 한다.toDataURL('image/jpeg', 0.5)은 유형을 통일적으로 jpeg로 설정하고 압축비는 스스로 제어합니다.
또 하나는 png이 jpg로 바뀌면 캔버스에 투명한 구역이 존재하고 jpg로 바뀌면 투명한 구역이 검은색으로 변한다. 캔버스의 투명한 픽셀은 기본적으로 rgba(0,0,0,0)이기 때문에 jpg로 바뀌면 rgba(0,0,0,1)로 변한다. 즉, 투명한 배경은 검은색으로 변한다.해결 방법은 그리기 전에 캔버스에 흰색 바탕색을 깔는 것이다.
  그림을 압축하기 전에 우리는 그림의 각도를 판단한다. 만약에 그림의 각도가 정확하지 않으면 EXIF를 사용해야 한다.js는 그림의 각도를 바로잡았다.
  그림을 압축하고base64의 그림 데이터를 이진 데이터로 변환하여 임시 저장소에 저장하고 getBlobList에 사용되기를 기다립니다.
Tip:canvas toDataURL jpg이 말은 잘 모르겠어요. 제가 표현하고 싶은 말은 이api가 jpeg이든 png이든 마지막에 내보낼 때 jpeg과 다를 게 없다는 거예요.png 그림의 투명성은 canvas에서 무효이기 때문에 canvas에 기본적인 검은색 배경을 추가합니다. 제가 설명할 때 흰색 배경으로 처리했기 때문에 마지막으로 내보낸 그림은 png이든 jpeg이든 모두 jpeg와 다를 것이 없습니다. 왜냐하면 png의 투명도를 유지할 수 없기 때문입니다.
(function(window) {

    /**
     * 
     *   :    
     * 
     *     :[email protected]
     * 
     *   :2017-10-26
     * 
     *     :    &&      &&     (Blob)          
     * 
     */

    window.lzImgProcess = function () {


        var Orientation = '', //     
            blobList = [], //             

            canvas = document.createElement("canvas"), //      (      ) canvas
            ctx = canvas.getContext('2d'),

            file_type = 'image/jpeg', //    
            qlty = 0.5, //      ,   0.5,     0-1       ,   
            imgWH = 1000; //              ,   1000px,   


        /**
         * @actionName process, 
         *            :    &&      &&     (Blob)     
         * 
         * @param fileList,           ,fileList                     FileList  
         *          :       jpeg||png
         *          : 
         *              function loadImageFile() {
         *                //     fileList  
         *                var fileList = document.getElementById("uploadImage").files;
         *              }
         * @param getBlobList [Blob],           ,      。
         *          :        ,                    ,   :           (blob)   list
         *        
         * @param quality,           (  ),    0-1       ,   0.5
         *
         * @param WH,                    ,   1000,   px,     。
         *          :      1000,    ,    canvas    。        ,       。1000*1000       ,      。
         *        
         */
        function process (fileList, getBlobList, quality, WH) {
            blobList = []; //   blobList
            //     fileList       0
            if (!fileList.length){
                console.log('  :    process   fileList       !!!')
                return;
            }

            //  quality    ,  quality   qlty(       )
            if(quality)
                qlty = quality;

            //  WH    ,  WH   imgWH(              )
            if(WH&&WH<1000&&WH>0){
                imgWH = WH;
            }

            //      fileList      
            var files = Array.prototype.slice.call(fileList);
            
            files.forEach(function (file, i) {
                if (!/\/(?:jpeg|png)/i.test(file.type)){
                    console.log('  :     jpeg||png  !!!');
                    return;
                }
                // file_type = file.type;
                
                var reader = new FileReader();

                //          ,         
                var size = file.size/1024 > 1024 ? (~~(10*file.size/1024/1024))/10 + "MB" :  ~~(file.size/1024) + "KB";
                // console.log('size:', size)

                reader.onload = function () {
                    var img = new Image();
                    img.src = this.result;

                    //             
                    if (img.complete) {
                        callback();
                    } else {
                        img.onload = callback;
                    }

                    function callback() {
                        //         ,        
                        EXIF.getData(img, function() {
                            // alert(EXIF.pretty(this));
                            EXIF.getAllTags(this);   
                            // alert(EXIF.getTag(this, 'Orientation'));
                            Orientation = EXIF.getTag(this, 'Orientation'); 
							console.log('Orientation:', Orientation)
                            if(Orientation == ""||Orientation == undefined||Orientation == null){
                                Orientation = 1;
                            }
                        });

                        //             
                        var data = GetImgCompress(img);

                        //                 
                        blobList.push(data);

                        //              (blob)   list          
                        if(blobList.length===files.length){
                            if(getBlobList)
                                getBlobList(blobList);
                        }
                        
                        img = null;
                    }

                };

                reader.readAsDataURL(file);
            })
        }


        /**
         * @actionName GetImgCompress,
         *       :         ,       ,    ,        ,    ,             
         *     
         * @param img,          
         * 
         * @returns               
         */
        function GetImgCompress(img){
            //       1,       
            if(Orientation != 1){
                switch(Orientation){
                    case 6://     90   
                        rotateImg(img,'right',canvas);
                        break;
                    case 8://     90   
                        rotateImg(img,'left',canvas);
                        break;
                    case 3://  180   
                        rotateImg(img,'right2',canvas);//   
                        break;
                }
            }else{
                //    
                rotateImg(img,'no',canvas);
            }

            var ndata;
            
            ndata = canvas.toDataURL(file_type, qlty);
            
            //         ,      
            // var initSize = img.src.length;
            // console.log('   :' + initSize);
            // console.log('   :' + ndata.length, 'base64  ', ndata);
            // console.log('   :' + ~~(100 * (initSize - ndata.length) / initSize) + "%");


            //     base64         
            ndata = dataURItoBlob(ndata);

            //  canvas     
            canvas.width = canvas.height = 0;
            
            return ndata;
        }



        /**
         * @actionName rotateImg,
         *       :       
         *     
         * @param img,            
         * 
         * @param direction,     
         *
         * @param canvas,        cavas    
         */
        function rotateImg(img, direction,canvas) {    
            
            //         ,    4           
            var min_step = 0;
            var max_step = 3;
            if (img == null)return;    
            //img         img       ,         
            var height = img.height;    
            var width = img.width;

            if(width>imgWH || height>imgWH){
                var ratio = ~~(height/width*10)/10;
                if(width>height){
                    width = imgWH;
                    height = imgWH*ratio;
                }else{
                    height = imgWH;
                    width = height/ratio;
                }
                img.width = width;
                img.height = height;
            }

            var step = 2;    
            if (step == null) {    
                step = min_step;    
            }
            if (direction == 'no'){
                step = 0;    
            } else if (direction == 'left') {    
                step++;    
                //      ,          
                step > max_step && (step = min_step);    
            } else if (direction == 'right') {			
				step--;    
                step < min_step && (step = max_step);    
			} else {    
                //  180 
            }

            //               
            var degree = step * 90 * Math.PI / 180; 

            switch (step) {
                case 0:
                    canvas.width = width;    
                    canvas.height = height;   

                    //    
                    ctx.fillStyle = "#fff";
                    ctx.fillRect(0, 0, width, height);
                    ctx.drawImage(img, 0, 0, width, height);   
                    break;    
                case 1:
                    canvas.width = height;    
                    canvas.height = width;   

                    //    
                    ctx.fillStyle = "#fff";
                    ctx.fillRect(0, 0, height, width);
                    ctx.rotate(degree);    
                    ctx.drawImage(img, 0, -height, width, height);    
                    break;
                case 2:
                    canvas.width = width;    
                    canvas.height = height;   

                    //    
                    ctx.fillStyle = "#fff";
                    ctx.fillRect(0, 0, width, height);
                    ctx.rotate(degree);    
                    ctx.drawImage(img, -width, -height, width, height);    
                    break;    
                case 3:
                    canvas.width = height;    
                    canvas.height = width;   

                    //    
                    ctx.fillStyle = "#fff";
                    ctx.fillRect(0, 0, height, width);
                    ctx.rotate(degree);    
                    ctx.drawImage(img, -width, 0, width, height);    
                    break;
            }
        }




        /**
         * dataURL to blob, ref to https://gist.github.com/fupslot/5015897
         * @param dataURI,   base64    
         * @returns {Blob}
         */
        function dataURItoBlob(dataURI) {
            var byteString = atob(dataURI.split(',')[1]);
            var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
            var ab = new ArrayBuffer(byteString.length);
            var ia = new Uint8Array(ab);
            for (var i = 0; i < byteString.length; i++) {
                ia[i] = byteString.charCodeAt(i);
            }
            return new Blob([ab], {type: mimeString});
        }




        /**
         *     process  
         * 
         * process  :        ,        ,     correctOrientation        。
         * 
         */
        return process;


    }
})(window)

Exif.js는 자바스크립트가 이미지를 읽는 원시 데이터의 기능 확장을 제공했다. 예를 들어 촬영 방향, 카메라 장치 모델, 촬영 시간, ISO 감광도, GPS 지리적 위치 등이다.Exif.js 공식github 창고 주소:github.com/exif-js/exi…
【3】 압축된 그림의 2진 데이터를 가져와 그림을 미리 보기
그림의 압축을 완성하면 압축된 그림의 2진 데이터를 얻을 수 있고 얻은 그림의 2진 데이터를 저장할 수 있다.데이터를 가져오면 미리 볼 수 있습니다.
실제 프로젝트에서 프로젝트마다 UI 스타일의 디자인이 다를 수 있기 때문에 개발자는 자신의 UI 스타일에 따라 그림을 미리 볼 수 있습니다.
//        
function getCompressiveFileList(fileList) {
    blobFileList = fileList;
    // console.log('fileBlobList:', fileList);
    fileList.forEach(function (blob) {
        var reader = new FileReader();
        reader.onload = function () {
            var li = document.createElement("LI")
            li.style.backgroundImage = 'url('+this.result+')';
            document.querySelector('.imgCompress_list').appendChild(li)
        }
        reader.readAsDataURL(blob);
    })
}

【4】 이미지 데이터를 백엔드에 제출한다.
formdata 대상물론 네이티브 아날로그로도 올릴 수 있습니다.
//             append formdata           
//  :    formdata  ,         ,   formdata           
function formUpData(blobFiles){
    var formData = new FormData();

    formData.append("files", blobFiles);
    
    var xhr = new XMLHttpRequest();
    
    //             ,       ,      ,      ,      
    xhr.open('post', 'http://xxx/welcome/index/');

    xhr.onreadystatechange = function () {
        if (xhr.readyState == 4 && xhr.status == 200) {
                console.log('    !');
        }
    };

    xhr.send(formData);
}

결어
전체 코드 및 데모:git 창고 주소
글에서 H5의 지식을 사용했는데 모르는 어린이 신발은 아래의 지식점 자료 주소로 들어가 HTML Canvas Element을 자세히 읽을 수 있다.toDataURL()CanvasRenderingContext2D.drawImage()FileReader()FormDataBlob()EXIF.js 중국어 블로그 글
이 글을 좋아하신다면 저를 주목해 주십시오. 저는 기술 문장을 지속적으로 갱신하여 제 굴금 홈페이지에 올릴 것입니다.감사합니다 응원~
커뮤니케이션 프런트엔드 기술을 선호하는 경우 내 QQ 커뮤니티 추가: 583575104 또는 366420656

좋은 웹페이지 즐겨찾기