圖片上傳預(yù)覽原理及實(shí)現(xiàn)

目前網(wǎng)上有很多支持圖片上傳時(shí)進(jìn)行預(yù)覽的插件,功能完備航攒,界面優(yōu)雅,使用起來(lái)也很方便趴梢。一直以來(lái)也就只是用用漠畜,沒(méi)有想過(guò)這些插件背后的實(shí)現(xiàn)原理。趁著今天有點(diǎn)時(shí)間坞靶,也來(lái)學(xué)習(xí)學(xué)習(xí)憔狞。


[toc]

追根溯源

設(shè)想

一開始,按照我的思路彰阴,預(yù)覽可能是這么來(lái)實(shí)現(xiàn)的瘾敢。本地選中一張圖片,嵌入html的同時(shí)會(huì)顯示圖片的本地的絕對(duì)路徑尿这,然后通過(guò)js簡(jiǎn)單的進(jìn)行設(shè)置簇抵,應(yīng)該就可以實(shí)現(xiàn)預(yù)覽效果了。

但是實(shí)際上射众,目前只有低版本的IE瀏覽器才能實(shí)現(xiàn)這么個(gè)效果碟摆。究其原因是瀏覽器廠商為了進(jìn)一步強(qiáng)化安全,限制了file標(biāo)簽直接讀取本地路徑的能力叨橱,在HTML5下只有通過(guò)FileReader的API來(lái)實(shí)現(xiàn)這一需求了典蜕。
比如對(duì)于CSDN寫博客的時(shí)候上傳一張圖片,得到的只會(huì)是一個(gè)fakepath罗洗。有圖為證:

file標(biāo)簽被限制讀取本地絕對(duì)路徑

原理

FileReader就是html5為我們提供的讀取文件的api愉舔。它的作用就是把文本流按指定格式讀取到緩存,以供js調(diào)用伙菜。

FileReader有四種讀取文件的方式:

  1. readAsBinaryString讀取為二進(jìn)制碼

  2. readAsDataURL讀取為 DataURL

  3. readAsText讀取為文本

  4. readAsArrayBuffer

根據(jù)本次實(shí)現(xiàn)的目標(biāo)轩缤,使用第二種方式即可。img標(biāo)簽的src就是這個(gè)圖片的編碼后的DataURL仇让。如圖所示:

編碼后的圖片src

DataURL淺析

DataURL 說(shuō)來(lái)可是有很多內(nèi)容要研究的典奉,但是這次用的比較淺顯,就把基礎(chǔ)的了解下就行了丧叽。

格式

DataURL有其固定的格式卫玖,如下:

data:[文件格式];base64,[文本流base64編碼]。

舉個(gè)例子:

  • jpg格式: data:image/jpeg;base64,/9j/4...
  • png格式: data:image/png;base64,iVBORw...
  • gif格式: data:image/gif;base64,R0lGOD...

  • png格式的圖片編碼信息
png 格式的編碼格式

預(yù)覽實(shí)現(xiàn)

好了踊淳,弄明白了這些原理性的東西假瞬,就可以著手進(jìn)行實(shí)現(xiàn)了陕靠。

HTML


<form action="#" method="POST">
    <legend>
        圖片上傳
    </legend>
    <fieldset>
        <input type="file" name="pic1" id="pic1" onchange="preview(this)" multiple="multiple"
               accept="image/x-png, image/jpg, image/jpeg, image/gif">
        <br><br>
    </fieldset>
    <input type="button" value="上傳">
</form>
<div id="container">


</div>

在代碼中使用了HTML5的一些新特性。用來(lái)過(guò)濾待上傳的圖片格式脱茉。

JavaScript控制

接下來(lái)就是預(yù)覽功能的實(shí)現(xiàn)了剪芥。目標(biāo)就是將圖片轉(zhuǎn)換成DataURL,然后對(duì)預(yù)覽區(qū)進(jìn)行子元素的添加操作琴许。


<script>
    var msg = "您可以上傳png, jpg, 或者gif格式的圖片";
    var filter = {
        "jpeg": "/9j/4",
        "gif": "R0lGOD",
        "png": "iVBORw"
    };
    function preview(file) {
        var container = document.getElementById("container");
        container.innerHTML = "";
        if (window.FileReader) {
            for (var index=0, f; f = file.files[index]; index++) {

                var filereader = new FileReader();
                filereader.onload = function (event) {
                    var srcpath = event.target.result;
                    if (!validateImg(srcpath)) {
                        console.log("H5"+msg);
                    } else {
                        showPreviewImage(srcpath);
                    }
                };
                filereader.readAsDataURL(f);
            }
        } else {
            if (!/\.jpg$|\.png$|\.gif$/i.test(file.value)) {
                console.log("原生"+msg);
            } else {
                showPreviewImage(file.value);
            }
        }
    }

    function validateImg(data) {
        console.log(data);
        var pos = data.indexOf(",") + 1;
        for (var e in filter) {
            if (data.indexOf(filter[e]) === pos) {
                return e;
            }
        }
        return null;
    }

    function showPreviewImage(src) {
        console.log(src);


        var img = document.createElement('img');
        img.src = src;
        img.style = "width:64px;height:auto;"
        container.appendChild(img);
    }

</script>

預(yù)覽效果

總的來(lái)說(shuō)代碼就算是完成了税肪,接下來(lái)看下實(shí)現(xiàn)的效果。由于沒(méi)有設(shè)置樣式榜田,所以看起來(lái)很簡(jiǎn)陋益兄,有興趣的自己用樣式控制一下即可。


圖片上傳預(yù)覽實(shí)現(xiàn)

打包封裝

簡(jiǎn)易封裝

為了方便實(shí)用箭券,特使用原生JavaScript封裝了一個(gè)這樣的組件净捅。詳細(xì)代碼如下:

/**
 * Created by biao on 2017/7/10.
 * Description: A simple tool for previewing images for uploading.
 * Blog: http://blog.csdn.net/marksinoberg
 * GitHub: https://github.com/guoruibiao
 */

function ImgPrevirewer(config) {

    /**
     * The tag ID for upload images.
     */
    this.fileId = config.fileId;

    /**
     * tip for error message.
     * @type {string}
     */
    this.tip = config.tip;
    /**
     * The ID for the container which contains img tags.
     * @type {string}
     */
    this.containerId = config.containerId;
    /**
     * CSS style for previewing imgs.
     * @type {string}
     */
    this.imgStyle = config.imgStyle;

    /**
     * 過(guò)濾圖片格式,可進(jìn)行相對(duì)應(yīng)的刪減操作辩块。
     * @type {{jpeg: string, gif: string, png: string}}
     */
    this.filter = {
        /**
         * jpg或者jpeg格式的圖片蛔六。
         */
        "jpeg": "/9j/4",
        /**
         * gif格式的圖片。
         */
        "gif": "R0lGOD",
        /**
         * PNG格式的圖片废亭。
         */
        "png": "iVBORw"
    };


    /**
     * 開始預(yù)覽国章。自動(dòng)調(diào)用原生JavaScript實(shí)現(xiàn)相關(guān)元素的定位以及渲染。
     */
    this.preview = function () {
        var file = document.getElementById(this.fileId);
        var container = document.getElementById(this.containerId);
        container.innerHTML = "";
        /**
         * 防止內(nèi)部作用域覆蓋問(wèn)題滔以。
         * @type {ImgPrevirewer}
         */
        var that = this;
        // HTML5 需要使用FileReader的相關(guān)API來(lái)讀取本地?cái)?shù)據(jù)捉腥。
        if (window.FileReader) {
            // 針對(duì)多個(gè)上傳文件批量處理。
            for (var index = 0, f; f = file.files[index]; index++) {
                var filereader = new FileReader();
                filereader.onload = function (event) {
                    var srcpath = event.target.result;
                    if (!that.validateImg(srcpath)) {
                        console.log(this.tip);
                    } else {
                        that.showPreviewImg(srcpath);
                    }
                };
                filereader.readAsDataURL(f);
            }
        } else {
            // 低版本降級(jí)處理你画。
            if (!/\.jpg$|\.png$|\.gif$/i.test(file.value)) {
                console.log(this.tip);
            } else {
                that.showPreviewImg(file.value);
            }
        }
    }


    /**
     * 根據(jù)圖片的base64編碼格式查看圖片是否符合要求抵碟。
     * @param data  編碼后的圖片數(shù)據(jù)。
     * @returns {*}
     */
    this.validateImg = function (data) {
        var pos = data.indexOf(",") + 1;
        for (var e in this.filter) {
            if (data.indexOf(this.filter[e]) === pos) {
                return e;
            }
        }
        return null;
    }

    /**
     * 開始實(shí)現(xiàn)對(duì)圖片的預(yù)覽坏匪,根據(jù)this.imgStyle進(jìn)行相關(guān)渲染操作拟逮。
     * @param src
     */
    this.showPreviewImg = function (src) {
        var img = document.createElement('img');
        img.src = src;
        img.style = this.imgStyle;
        container.appendChild(img);
    }


}




使用方式

下面來(lái)一個(gè)簡(jiǎn)單的“模板式”使用技巧。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Test</title>
    <script src="img-previewer.js"></script>
</head>
<body>
<input type="file" id="file" multiple onchange="preview()">
<div id="container">

</div>

<script>

    function preview(){
        var config = {
            tip: "請(qǐng)上傳格式為png, gif或者jpg的圖片",
            fileId: "file",
            containerId: "container",
            imgStyle: "width:320px;height:auto;border-radius:64%;"
        }
        var previewer = new ImgPrevirewer(config);
        previewer.preview();
    }

</script>
</body>
</html>

測(cè)試

為了保證這個(gè)組件的穩(wěn)定性适滓,接下來(lái)來(lái)個(gè)簡(jiǎn)單的測(cè)試敦迄。

  • 首先是在Chrome瀏覽器上,發(fā)現(xiàn)可以正常工作凭迹。
測(cè)試組件穩(wěn)定性
  • 接下來(lái)是在Edge瀏覽器上的測(cè)試罚屋。(發(fā)現(xiàn)樣式不兼容)


    Edge瀏覽器功能實(shí)現(xiàn)了,但是樣式不夠兼容
  • 不出所料嗅绸,IE系的瀏覽器樣式都沒(méi)能兼容脾猛。


    IE瀏覽器樣式不兼容

。鱼鸠。猛拴。

最終發(fā)現(xiàn)羹铅,Chrome等WebKit內(nèi)核的瀏覽器可以完美支持,對(duì)于微軟系瀏覽器而言愉昆,功能可以滿足职员,但是樣式上不兼容,這點(diǎn)可以通過(guò)特定的瀏覽器頭來(lái)實(shí)現(xiàn)跛溉,不再過(guò)多敘述焊切。

總結(jié)

總的來(lái)說(shuō),關(guān)于圖片上傳時(shí)的預(yù)覽功能芳室,實(shí)用性還是很強(qiáng)的蛛蒙。對(duì)于一個(gè)網(wǎng)站可以算是一個(gè)加分項(xiàng)。當(dāng)然了渤愁,該網(wǎng)站有一個(gè)設(shè)計(jì)感不錯(cuò)的美工或者前端,不像我做出的頁(yè)面好難看(⊙﹏⊙)b深夯。

大概就是這樣咯抖格,有需要的盡管拿去使用。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末咕晋,一起剝皮案震驚了整個(gè)濱河市雹拄,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌掌呜,老刑警劉巖滓玖,帶你破解...
    沈念sama閱讀 218,284評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異质蕉,居然都是意外死亡势篡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門模暗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)禁悠,“玉大人,你說(shuō)我怎么就攤上這事兑宇“欤” “怎么了?”我有些...
    開封第一講書人閱讀 164,614評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵隶糕,是天一觀的道長(zhǎng)瓷产。 經(jīng)常有香客問(wèn)我,道長(zhǎng)枚驻,這世上最難降的妖魔是什么濒旦? 我笑而不...
    開封第一講書人閱讀 58,671評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮测秸,結(jié)果婚禮上疤估,老公的妹妹穿的比我還像新娘灾常。我一直安慰自己,他們只是感情好铃拇,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評(píng)論 6 392
  • 文/花漫 我一把揭開白布钞瀑。 她就那樣靜靜地躺著,像睡著了一般慷荔。 火紅的嫁衣襯著肌膚如雪雕什。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,562評(píng)論 1 305
  • 那天显晶,我揣著相機(jī)與錄音贷岸,去河邊找鬼。 笑死磷雇,一個(gè)胖子當(dāng)著我的面吹牛偿警,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播唯笙,決...
    沈念sama閱讀 40,309評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼螟蒸,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了崩掘?” 一聲冷哼從身側(cè)響起七嫌,我...
    開封第一講書人閱讀 39,223評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎苞慢,沒(méi)想到半個(gè)月后诵原,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,668評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡挽放,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評(píng)論 3 336
  • 正文 我和宋清朗相戀三年绍赛,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片骂维。...
    茶點(diǎn)故事閱讀 39,981評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡惹资,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出航闺,到底是詐尸還是另有隱情褪测,我是刑警寧澤,帶...
    沈念sama閱讀 35,705評(píng)論 5 347
  • 正文 年R本政府宣布潦刃,位于F島的核電站侮措,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏乖杠。R本人自食惡果不足惜分扎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望胧洒。 院中可真熱鬧畏吓,春花似錦墨状、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至宏悦,卻和暖如春镐确,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背饼煞。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工源葫, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人砖瞧。 一個(gè)月前我還...
    沈念sama閱讀 48,146評(píng)論 3 370
  • 正文 我出身青樓息堂,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親块促。 傳聞我的和親對(duì)象是個(gè)殘疾皇子储矩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容