1. 程式人生 > >移動前端—圖片壓縮上傳實踐

移動前端—圖片壓縮上傳實踐

   此前有同事跟我聊過關於移動端用canvas壓縮圖片後再上傳的功能,最近有了點空閒時間,所以就實踐了一下。demo效果連結在文章底部貼出。

  在做移動端圖片上傳的時候,使用者傳的都是手機本地圖片,而本地圖片一般都相對比較大,拿iphone6來說,平時拍很多圖片都是一兩M的,如果直接這樣上傳,那圖片就太大了,如果使用者用的是移動流量,完全把圖片上傳顯然不是一個好辦法。

  目前來說,HTML5的各種新API都在移動端的webkit上得到了較好的實現。根據檢視caniuse,本demo裡使用到的FileReader、Blob、Formdata物件均已在大部分移動裝置瀏覽器中得到了實現(safari6.0+、android 3.0+),所以直接在前端壓縮圖片,已經成了很多移動端圖片上傳的必備功能了。

  在移動端壓縮圖片並且上傳主要用到filereader、canvas 以及 formdata 這三個h5的api。邏輯並不難。整個過程就是:

  (1)使用者使用input file上傳圖片的時候,用filereader讀取使用者上傳的圖片資料(base64格式)

  (2)把圖片資料傳入img物件,然後將img繪製到canvas上,再呼叫canvas.toDataURL對圖片進行壓縮

  (3)獲取到壓縮後的base64格式圖片資料,轉成二進位制塞入formdata,再通過XmlHttpRequest提交formdata。

  如此三步,就完成了圖片的壓縮和上傳。

  說起來好像挺簡單,其實還是有些坑的。接下來就直接用程式碼進行分析:

  【一】獲取圖片資料

  先是獲取圖片資料,也就是監聽input file的change事件,然後獲取到上傳的檔案物件files,將類陣列的files轉成陣列,然後進行forEach遍歷。

  接著判斷檔案型別,如果不是圖片則不作處理。如果是圖片就例項化一個filereader,以base64格式讀取上傳的檔案資料,判斷資料長度,如果大於200KB的圖片就呼叫compress方法進行壓縮,否則呼叫upload方法進行上傳。

filechooser.onchange = function () {
        if (!this.files.length) return;

        
var files = Array.prototype.slice.call(this.files); if (files.length > 9) { alert("最多同時只可上傳9張圖片"); return; } files.forEach(function (file, i) { if (!/\/(?:jpeg|png|gif)/i.test(file.type)) return; var reader = new FileReader(); var li = document.createElement("li"); li.innerHTML = '<div class="progress"><span></span></div>'; $(".img-list").append($(li)); reader.onload = function () { var result = this.result; var img = new Image(); img.src = result; //如果圖片大小小於200kb,則直接上傳 if (result.length <= maxsize) { $(li).css("background-image", "url(" + result + ")"); img = null; upload(result, file.type, $(li)); return; } // 圖片載入完畢之後進行壓縮,然後上傳 if (img.complete) { callback(); } else { img.onload = callback; } function callback() { var data = compress(img); $(li).css("background-image", "url(" + data + ")"); upload(data, file.type, $(li)); img = null; } }; reader.readAsDataURL(file); }) };

  【2】壓縮圖片

  上面做完圖片資料的獲取後,就可以做compress壓縮圖片的方法了。而壓縮圖片也並不是直接把圖片繪製到canvas再呼叫一下toDataURL就行的。

  在IOS中,canvas繪製圖片是有兩個限制的:

  首先是圖片的大小,如果圖片的大小超過兩百萬畫素,圖片也是無法繪製到canvas上的,呼叫drawImage的時候不會報錯,但是你用toDataURL獲取圖片資料的時候獲取到的是空的圖片資料。

  再者就是canvas的大小有限制,如果canvas的大小大於大概五百萬畫素(即寬高乘積)的時候,不僅圖片畫不出來,其他什麼東西也都是畫不出來的。

  應對第一種限制,處理辦法就是瓦片繪製了。瓦片繪製,也就是將圖片分割成多塊繪製到canvas上,我程式碼裡的做法是把圖片分割成100萬畫素一塊的大小,再繪製到canvas上。

  而應對第二種限制,我的處理辦法是對圖片的寬高進行適當壓縮,我程式碼裡為了保險起見,設的上限是四百萬畫素,如果圖片大於四百萬畫素就壓縮到小於四百萬畫素。四百萬畫素的圖片應該夠了,算起來寬高都有2000X2000了。

  如此一來就解決了IOS上的兩種限制了。

  除了上面所述的限制,還有兩個坑,一個就是canvas的toDataURL是隻能壓縮jpg的,當用戶上傳的圖片是png的話,就需要轉成jpg,也就是統一用canvas.toDataURL('image/jpeg', 0.1) , 型別統一設成jpeg,而壓縮比就自己控制了。

  另一個就是如果是png轉jpg,繪製到canvas上的時候,canvas存在透明區域的話,當轉成jpg的時候透明區域會變成黑色,因為canvas的透明畫素預設為rgba(0,0,0,0),所以轉成jpg就變成rgba(0,0,0,1)了,也就是透明背景會變成了黑色。解決辦法就是繪製之前在canvas上鋪一層白色的底色。

function compress(img) {
        var initSize = img.src.length;
        var width = img.width;
        var height = img.height;

        //如果圖片大於四百萬畫素,計算壓縮比並將大小壓至400萬以下
        var ratio;
        if ((ratio = width * height / 4000000)>1) {
            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萬則使用瓦片繪製
        var count;
        if ((count = width * height / 1000000) > 1) {
            count = ~~(Math.sqrt(count)+1); //計算要分成多少塊瓦片

//            計算每塊瓦片的寬和高
            var nw = ~~(width / count);
            var nh = ~~(height / count);

            tCanvas.width = nw;
            tCanvas.height = nh;

            for (var i = 0; i < count; i++) {
                for (var 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);
        }

        //進行最小壓縮
        var ndata = canvas.toDataURL('image/jpeg', 0.1);

        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;
    }

  【三】圖片上傳

  完成圖片壓縮後,就可以塞進formdata裡進行上傳了,先將base64資料轉成字串,再例項化一個ArrayBuffer,然後將字串以8位整型的格式傳入ArrayBuffer,再通過BlobBuilder或者Blob物件,將8位整型的ArrayBuffer轉成二進位制物件blob,然後把blob物件append到formdata裡,再通過ajax傳送給後臺即可。

  XmlHttpRequest2中不僅可以傳送大資料,還多出了比如獲取傳送進度的API,我程式碼裡也進行了簡單的實現。

//    圖片上傳,將base64的圖片轉成二進位制物件,塞進formdata上傳
    function upload(basestr, type, $li) {
        var text = window.atob(basestr.split(",")[1]);
        var buffer = new ArrayBuffer(text.length);
        var ubuffer = new Uint8Array(buffer);
        var pecent = 0 , loop = null;

        for (var i = 0; i < text.length; i++) {
            ubuffer[i] = text.charCodeAt(i);
        }

        var Builder = window.WebKitBlobBuilder || window.MozBlobBuilder;
        var blob;

        if (Builder) {
            var builder = new Builder();
            builder.append(buffer);
            blob = builder.getBlob(type);
        } else {
            blob = new window.Blob([buffer], {type: type});
        }

        var xhr = new XMLHttpRequest();
        var formdata = new FormData();
        formdata.append('imagefile', blob);

        xhr.open('post', '/cupload');

        xhr.onreadystatechange = function () {
            if (xhr.readyState == 4 && xhr.status == 200) {
                console.log('上傳成功:' + xhr.responseText);

                clearInterval(loop);

                //當收到該訊息時上傳完畢
                $li.find(".progress span").animate({'width': "100%"}, pecent < 95 ? 200 : 0, function () {
                    $(this).html("上傳成功");
                });

                $(".pic-list").append('<a href="' + xhr.responseText + '">' + xhr.responseText + '<img src="' + xhr.responseText + '" /></a>')
            }
        };

        //資料傳送進度,前50%展示該進度
        xhr.upload.addEventListener('progress', function (e) {
            if (loop) return;

            pecent = ~~(100 * e.loaded / e.total) / 2;
            $li.find(".progress span").css('width', pecent + "%");

            if (pecent == 50) {
                mockProgress();
            }
        }, false);

        //資料後50%用模擬進度
        function mockProgress() {
            if (loop) return;

            loop = setInterval(function () {
                pecent++;
                $li.find(".progress span").css('width', pecent + "%");

                if (pecent == 99) {
                    clearInterval(loop);
                }
            }, 100)
        }

        xhr.send(formdata);
    }

  至此,整個上傳的前端圖片壓縮就完成了,因為是用了formdata提交,所以後臺接資料的時候就跟普通form表單提交資料一樣處理即可。

  如果對該demo有興趣的可以看這個demo的github地址:

相關推薦

移動前端圖片壓縮實踐

   此前有同事跟我聊過關於移動端用canvas壓縮圖片後再上傳的功能,最近有了點空閒時間,所以就實踐了一下。demo效果連結在文章底部貼出。   在做移動端圖片上傳的時候,使用者傳的都是手機本地圖片,而本地圖片一般都相對比較大,拿iphone6來說,平時拍很多圖片都是一兩M的,如果直接這樣上傳,那圖片就太

移動前端圖片壓縮

safari 嘻嘻 如果 tee ini andro 並且 ons create   摘要:之前在做一個小遊戲平臺項目,有個“用戶中心”模塊,就涉及到了頭像上傳的功能。在做移動端圖片上傳的時候,傳的都是手機本地圖片,而本地圖片一般都相對比較大,拿現在的智能手機來說,平時拍很

移動圖片壓縮解決方案

長度 繪制圖片 slice ase 但是 choose 100萬 lis 計算 最近做移動端圖片上傳,發現圖片尤其是iPhone拍照的圖片都有2M左右,但是實際上項目中用不到這麽大,於是想到要用js在前臺進行壓縮。 解決方案如下:  【一】獲取圖片數據   先是獲取圖片數據

前端圖片壓縮(純js的質量壓縮,非大小壓縮)

默認 || callback doc 圖片格式 toc jpeg rtb src 此demo為大於1M對圖片進行壓縮上傳 若小於1M則原圖上傳,可以根據自己實際需求更改。 demo源碼如下: <!DOCTYPE html> <html> <h

前端圖片壓縮和顯示

最近在寫一個修改頭像的功能,涉及到圖片的壓縮上傳和顯示,主要用到了以下幾個知識點: 利用input [type=”file”]上傳圖片 利用FileReader的readAsDataURL讀取圖片將其轉為base64 當圖片很大的時候利用canvas進行圖片

JS+HTML5實現前端圖片壓縮到騰訊的COS

<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <meta name="viewport" content="w

前端js壓縮圖片 多圖、單圖 ajax

<script> /* 三個引數 file:一個是檔案(型別是圖片格式), w:一個是檔案壓縮的後寬度,寬度越小,位元組越小 objDiv:一個是容器或者回調函式 phot

vue裏圖片壓縮組件

UNC cep accept posit click toa dcl v-on ati //單圖上傳 <template> <div> <div class="uploader" v-if=‘!dwimg‘&g

TP框架圖片壓縮/

<-- 在前端的程式碼 --><form action="{:url('index/user/personal')}" method="post" enctype="multipart/form-data"> <input type="file" name="image"

圖片 壓縮 mongodb和下載

// 獲得SpringBoot提供的mongodb的GridFS物件 @Autowired private GridFsTemplate gridFsTemplate; public ServiceResult<FileInfoAO> compressUplo

JS—圖片壓縮(單張)

eight ascii value size set tsa ade ready chan *vue+webpack環境,這裏的that指到vue實例 &lt;input type="file" name="file" ac

JQ圖片壓縮

css * { margin: 0; padding: 0; } li { list-style-type: none; } a, input { outline: none; -webkit-tap-high

Spring 使用七牛雲端儲存圖片以及html5圖片壓縮

需求: 最近在做專案的時候採用了多模組的方案 前臺是一個系統 後臺是另一個系統 在做圖片上傳的時候有個問題 如果想之前那樣前臺系統的圖片儲存的自己的web應用目錄下 後臺系統是沒法訪問的 (直接寫死路徑不太好) 這裡我想到了兩個方案 方案一:自己搭建

vue移動圖片裁剪

1. 安裝cropperjs依賴庫 npm install cropperjs 2. 編寫元件SimpleCropper.vue <template> <div class="v-simple-cropper"> <slot>

Android中相機拍攝照片,以及相簿選擇圖片壓縮(壓縮後儲存進SD中)(可用於修改頭像等)

           這個功能借鑑了多個大神的,然後整合起來。                    Android中相機拍攝照片,以及相簿選擇圖片壓縮上傳(壓縮後儲存進SD中)(可用   於修改頭像等)                       第一步: privat

Vue directive自定義指令+canvas實現H5圖片壓縮-Base64格式

前言 最近優化專案-手機拍照圖片太大,回顯速度比較慢,使用了vue的自定義指令實現H5壓縮上傳base64格式的圖片 canvas自定義指令 Vue.directive("canvas",

HTML5移動圖片瀏覽

$(function(){ //壓縮後圖片陣列 var files=new Array(); //用於壓縮圖片的canvas var canvas = document.createElement("canvas"); var ctx = canvas.getCo

H5+jqweui實現手機端圖片壓縮 Base64

主要功能,使用H5的formData上傳base64格式的圖片,canvas壓縮圖片,前端樣式使用weui,為方便起見,使用了jquery封裝過的weui,jqweui。話不多少,開始上程式碼。前端程式碼,直接在jqweui官網下的demo裡改的(是dist下的demo)<!DOCTYPE html&g

H5+jqweui實現手機端圖片壓縮

主要功能,使用H5的formData上傳base64格式的圖片,canvas壓縮圖片,前端樣式使用weui,為方便起見,使用了jquery封裝過的weui,jqweui。 話不多少,開始上程式碼。 前端程式碼,直接在jqweui官網下的demo裡改的(是dist下的demo) <!DOCTYPE ht

移動圖片裁剪—jQuery.cropper.js

The new ++ spec 得到 end ipad 安卓 java jQuery.cropper.js是一款使用簡單且功能強大的圖片剪裁jQuery插件。該圖片剪裁插件支持圖片放大縮小,支持圖片旋轉,支持觸摸屏設備,支持canvas,並且支持跨瀏覽器使用。 一、移動端獲