Javascript:圖片預(yù)覽,壓縮及上傳

一、圖片預(yù)覽

圖片預(yù)覽有兩種方式:

  1. FileReader把圖片轉(zhuǎn)化為base64格式的數(shù)據(jù)嵌入到HTML中姑丑。
  2. URL.createObjectURL()構(gòu)造圖片的URL并賦值給<img>標(biāo)簽。

1.

FileReader允許瀏覽器異步讀取用戶電腦上的文件呀忧,需要用File或者Blob指定要讀取的文件师痕。

FileReader有一個(gè)方法叫.readAsDataURL(),這個(gè)方法用來(lái)讀取指定Blob或者File的內(nèi)容而账,讀取完成之后readyState會(huì)變成DONE胰坟,并且觸發(fā)loadend事件。

讀取完成之后泞辐,result屬性包含了一個(gè)url笔横,這個(gè)url是一個(gè)Data URLData URL以base64編碼的形式存儲(chǔ)文件的數(shù)據(jù)咐吼。

已知圖片的src屬性即可以接受圖片的url吹缔,也可以接受一個(gè)Data URL,直接把編碼后的圖片信息內(nèi)嵌到HTML中锯茄。

那么我們可以把這個(gè)Data URL賦值給img.src厢塘,這樣就可以在頁(yè)面上顯示圖片了。

代碼如下:

<!-- upload.html -->
<div>
    <button type="button" id="button">點(diǎn)擊添加圖片</button>
    <input type="file" id="chooseFile"> <!-- hidden屬性對(duì)其不起作用 -->
    <img src="" id="preview">
</div>
/* index.css */
/* 隱藏input框肌幽,因?yàn)樘罅送砟耄钊似v,這樣我們就可以定義自己美美的輸入框了 */
input {
  display: none;
}
// index.js
var log = console.log.bind(console)

$('#button').on('click', function () { // 點(diǎn)擊按鈕觸發(fā)input框的click事件
    $('#chooseFile').click()
})
$('#chooseFile').on('change', function () {
    var file = this.files[0]
    // log(file)
    var reader = new FileReader()
    reader.onload = function (e) {
        // log(e.target)
        $('#preview').attr('src', e.target.result) // 把圖片的base64編碼形式賦值給img.src
    }
    reader.readAsDataURL(file)
})

關(guān)于Data URLs

Data URLs由4個(gè)部分組成:

  1. 前綴:data:
  2. mediatype;:一個(gè)MINE類(lèi)型的聲明喂急,表示這串?dāng)?shù)據(jù)的編碼類(lèi)型格嘁,比如:image/png.如果缺省,默認(rèn)為text/plain;charset=US-ASCII
  3. 可選的base64,:如果該數(shù)據(jù)是非文本類(lèi)型的廊移,那么要加上這個(gè)部分
  4. 數(shù)據(jù)本身

舉個(gè)例子糕簿,這是一張圖片的Data URL:

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA1IAAASuCAYAAADI0RjrAAAMJGlDQ1BJQ0MgUHJvZmlsZQAASImVVwdUk8kWnr8kISGhBSIgJfQ......

Data URLs的好處是:

  1. 我們可以把體積比較小的圖片嵌入到HTML文件中,減少HTTP請(qǐng)求次數(shù)狡孔。
  2. 如果圖片是在服務(wù)端用程序動(dòng)態(tài)生成的懂诗,每個(gè)用戶顯示的都不同時(shí),直接把圖片的Data URL嵌入到網(wǎng)頁(yè)中會(huì)很方便步氏。
  3. 如果訪問(wèn)外部資源很麻煩或者受限時(shí)响禽。

Data URLs的缺點(diǎn)是:

  1. base64的體積會(huì)比原數(shù)據(jù)體積大約 1/3
  2. Data URL形式的圖片不會(huì)被瀏覽器緩存,這意味著每次訪問(wèn)頁(yè)面都要加載一次圖片荚醒。這種情況可以通過(guò)CSS文件把Data URL設(shè)成背景圖片的url來(lái)避免芋类。
    比如:
div { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA1IAAASuCAYAAADI0RjrAAAMJGlDQ1BJQ0MgUHJvZmlsZQAASImVVwdUk8kWnr8kISGhBSIgJfQ......")

關(guān)于base64:

Base64編碼原理與應(yīng)用
阮一峰的網(wǎng)絡(luò)日志——Base64筆記

2.

URL接口是一個(gè)包含若干靜態(tài)方法的對(duì)象。
URL用于解析界阁,構(gòu)建侯繁,規(guī)范化和編碼URLs。
URL接口可以在Web Worker中使用泡躯。

當(dāng)使用一個(gè)沒(méi)有實(shí)現(xiàn)該構(gòu)造器的用戶代理時(shí)贮竟,可以通過(guò) Window.URL屬性來(lái)訪問(wèn)該對(duì)象(基于 Webkit 和 Blink 內(nèi)核的瀏覽器均可用 Window.webkitURL 代替)丽焊。

URL = URL || window.URL || window.webkitURL

URL有一個(gè).createObjectURL()方法,該方法接收一個(gè)本地的blob作為參數(shù)咕别,創(chuàng)建一個(gè)指向該blob的url技健,在幾乎任何可以使用url的地方都可以使用.createObjectURL()方法創(chuàng)建的url。

這意味著惰拱,我們可以使用它來(lái)實(shí)現(xiàn)圖片的預(yù)覽:

// index.js
$('#chooseFile').on('change', function () {
    var file = this.files[0]
    var url = URL.createObjectURL(file) // 創(chuàng)建一個(gè)指向選擇的圖片的url
    // log(url)
    $('#preview').attr('src', url) // 將url賦值給img.src
})

.createObjectURL()創(chuàng)建的路徑的格式是這樣的:blob:null/元素本身組成的路徑雌贱。
比如:blob:null/8629b731-05a1-4b74-8a5a-b87878eba849
null表示域名為空。

URL.createObjectURL()的性能要比使用FileReader高很多偿短,如果只是單純想要預(yù)覽圖片欣孤,那么使用URL.createObjectURL()比較好。

二昔逗、圖片壓縮

canvas 有一個(gè) toDataURL(type, quantity)方法降传,就是把canvas的數(shù)據(jù)輸出為一個(gè)Data URL,該方法可以設(shè)置圖片質(zhì)量勾怒。利用這一特性婆排,就可以用來(lái)壓縮圖片。

function compress(img) { // img為圖片的Data URL
    var canvas = document.createElement('canvas') // 創(chuàng)建一個(gè)canvas
    var context = canvas.getContext('2d') 
    var width = img.width
    var height = img.height
    canvas.width = width // 把canvas的寬高設(shè)置為圖片的寬高
    canvas.height = height
    // 開(kāi)始畫(huà)圖
    context.fillStyle = '#fff'
    context.fillRect(0, 0, canvas.width, canvas.height)
    context.drawImage(img, 0, 0, canvas.width, canvas.height)
    var base64Data = canvas.toDataURL('image/jpeg', 0.4) // 壓縮圖片控硼,質(zhì)量參數(shù)太多反而會(huì)加大圖片體積泽论!
    canvas = context = null 
    // log(base64Data.length)
    img = null
    return base64Data // 返回壓縮后的base64字符串流
}

需要注意的是:

  1. canvas.toDataURL的質(zhì)量參數(shù)只對(duì)image/jpegimage/webp有效艾少。
  2. 如果圖像本身是image/png卡乾,則 type 參數(shù)不能為非image/png的其他類(lèi)型。
  3. 質(zhì)量參數(shù)默認(rèn)是0.92缚够,質(zhì)量參數(shù)太大反而會(huì)增加圖片的體積幔妨。

三、 圖片上傳

假設(shè)我們沒(méi)有壓縮圖片谍椅,那么直接使用FormData()上傳圖片就可以了误堡。
現(xiàn)在假設(shè)我們已經(jīng)壓縮了圖片,那么要怎么傳圖片呢雏吭?

可以直接把base64的數(shù)據(jù)傳到后端再解碼成圖片锁施。也可以在前端先解碼再傳到后端,因?yàn)閎ase64數(shù)據(jù)的體積要比原數(shù)據(jù)的體積大 1/3 杖们。

思路如下:

  1. .atob()方法對(duì)用base-64編碼過(guò)的字符串進(jìn)行解碼悉抵,我們現(xiàn)在輸出會(huì)得到一串二進(jìn)制字符串。
  2. .charCodeAt()方法得到每一個(gè)字符的Unicode編碼摘完。
  3. new Uint8Array();把我們得到的Unicode編碼轉(zhuǎn)化為類(lèi)型數(shù)組姥饰,類(lèi)型數(shù)組擁有二進(jìn)制支持,可以用來(lái)處理二進(jìn)制文件孝治。普通數(shù)組是無(wú)法生成二進(jìn)制文件的列粪。
  4. Blob()構(gòu)造函數(shù)構(gòu)造一個(gè)文件审磁。

代碼如下:

// 注意b64Data必須把前面的類(lèi)似于“data:image/png;base64,"的字串截掉
// contentType指圖片類(lèi)型,比如:image/png岂座, image/jpeg 等等
function b64ToBlob(b64Data, contentType) {
    contentType = contentType || ''

    var byteCharacters = atob(b64Data) // 解碼base64數(shù)據(jù)為二進(jìn)制字符串
    var buffer = [] // 注意态蒂,Blob第一個(gè)參數(shù)必須是一個(gè)數(shù)組

    // 類(lèi)型數(shù)組用來(lái)處理二進(jìn)制文件
    var aBuffer = new ArrayBuffer(byteCharacters.length)
    var uBuffer = new Uint8Array(aBuffer)
    for (var i = 0; i < byteCharacters.length; i++) {
        uBuffer[i] = byteCharacters.charCodeAt(i) // 得到Unicode編碼,存進(jìn)類(lèi)型數(shù)組
    }
    buffer.push(uBuffer)

    var blob = new Blob(buffer, { // 生成一個(gè)二進(jìn)制文件
        type: contentType
    })
    // log(blob)
    return blob
}

現(xiàn)在已經(jīng)完成了圖片的預(yù)覽费什,壓縮吃媒,以及壓縮后的轉(zhuǎn)碼。
之后就是ajax上傳的問(wèn)題了吕喘,先構(gòu)建一個(gè)FormData赘那,把圖片append進(jìn)去,傳到后臺(tái)氯质。最后的完整實(shí)現(xiàn):

<!--  index.html  -->
<body>

    <div id="imagesDiv">
        <input type="file" id="chooseFile">
    </div>

    <script src="../static/js/jquery-3.2.1.min.js"></script>

</body>
// index.js

var log = console.log.bind(console)

// 上傳圖片
$('#chooseFile').on('change', function () {
    var file = this.files[0]
    if (!file.type.match('image.*')) { // 不是圖片則返回
        alert('只能上傳圖片');
        return false;
    }
    var reader = new FileReader() 
    reader.onload = function () {
        var img = new Image()
        var originData = this.result

        img.onload = function () {
            var compressUrl = compress(img) // 壓縮圖片
            var blob = b64ToBlob(compressUrl.split(',')[1], 'image/jpeg') // 壓縮成jpeg格式
            var form_data = new FormData()
            form_data.append('file', blob)
            $.ajax({
                type: 'POST',
                url: '你的url',
                data: form_data,
                processData: false, // 必須
                contentType: false, // 必須
                success: function (data, status, xhr) {
                    console.log(data)
                },
                error: function (error) {
                    console.log(error)
                }
            })
        }
        img.src = originData // 把圖片數(shù)據(jù)賦值給img

    }
    reader.readAsDataURL(file) // 讀取圖片信息
})

// 壓縮圖片
function compress(img) {
    var canvas = document.createElement('canvas')
    var context = canvas.getContext('2d')
    var width = img.width
    var height = img.height
    // log(img.width, img.height)
    // log(width, height)
    canvas.width = width
    canvas.height = height
    context.fillStyle = '#fff'
    context.fillRect(0, 0, canvas.width, canvas.height)
    context.drawImage(img, 0, 0, canvas.width, canvas.height)
    var base64Data = canvas.toDataURL('image/jpeg', 0.4)
    canvas = context = null
    // log(base64Data.length)
    img = null
    return base64Data
}

// base64轉(zhuǎn)化為二進(jìn)制文件
function b64ToBlob(b64Data, contentType) {
    contentType = contentType || ''

    var byteCharacters = atob(b64Data) // 解碼base64數(shù)據(jù)為二進(jìn)制字符串
    var buffer = [] // 注意募舟,Blob第一個(gè)參數(shù)必須是一個(gè)數(shù)組

    // 類(lèi)型數(shù)組用來(lái)處理二進(jìn)制文件
    var aBuffer = new ArrayBuffer(byteCharacters.length)
    var uBuffer = new Uint8Array(aBuffer)
    for (var i = 0; i < byteCharacters.length; i++) {
        uBuffer[i] = byteCharacters.charCodeAt(i) // 得到Unicode編碼,存進(jìn)類(lèi)型數(shù)組
    }
    buffer.push(uBuffer)
    //  普通數(shù)組是無(wú)法生成二進(jìn)制文件的
    var blob = new Blob(buffer, { // 生成一個(gè)二進(jìn)制文件
        type: contentType
    })
    // log(blob)
    return blob
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末闻察,一起剝皮案震驚了整個(gè)濱河市拱礁,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌辕漂,老刑警劉巖呢灶,帶你破解...
    沈念sama閱讀 218,640評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異钉嘹,居然都是意外死亡鸯乃,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)跋涣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)缨睡,“玉大人,你說(shuō)我怎么就攤上這事陈辱〗蹦辏” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,011評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵沛贪,是天一觀的道長(zhǎng)陋守。 經(jīng)常有香客問(wèn)我,道長(zhǎng)利赋,這世上最難降的妖魔是什么水评? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,755評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮隐砸,結(jié)果婚禮上之碗,老公的妹妹穿的比我還像新娘。我一直安慰自己季希,他們只是感情好褪那,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,774評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布幽纷。 她就那樣靜靜地躺著,像睡著了一般博敬。 火紅的嫁衣襯著肌膚如雪友浸。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,610評(píng)論 1 305
  • 那天偏窝,我揣著相機(jī)與錄音收恢,去河邊找鬼。 笑死祭往,一個(gè)胖子當(dāng)著我的面吹牛伦意,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播硼补,決...
    沈念sama閱讀 40,352評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼驮肉,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了已骇?” 一聲冷哼從身側(cè)響起离钝,我...
    開(kāi)封第一講書(shū)人閱讀 39,257評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎褪储,沒(méi)想到半個(gè)月后卵渴,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,717評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鲤竹,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,894評(píng)論 3 336
  • 正文 我和宋清朗相戀三年浪读,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宛裕。...
    茶點(diǎn)故事閱讀 40,021評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡瑟啃,死狀恐怖论泛,靈堂內(nèi)的尸體忽然破棺而出揩尸,到底是詐尸還是另有隱情,我是刑警寧澤屁奏,帶...
    沈念sama閱讀 35,735評(píng)論 5 346
  • 正文 年R本政府宣布岩榆,位于F島的核電站,受9級(jí)特大地震影響坟瓢,放射性物質(zhì)發(fā)生泄漏勇边。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,354評(píng)論 3 330
  • 文/蒙蒙 一折联、第九天 我趴在偏房一處隱蔽的房頂上張望粒褒。 院中可真熱鬧,春花似錦诚镰、人聲如沸奕坟。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,936評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)月杉。三九已至刃跛,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間苛萎,已是汗流浹背桨昙。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,054評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留腌歉,地道東北人蛙酪。 一個(gè)月前我還...
    沈念sama閱讀 48,224評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像翘盖,于是被迫代替她去往敵國(guó)和親滤否。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,974評(píng)論 2 355

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

  • 參考1-HTML5實(shí)現(xiàn)圖片壓縮上傳功能參考2-移動(dòng)前端—圖片壓縮上傳實(shí)踐參考3-移動(dòng)端H5圖片壓縮上傳 大體步驟 ...
    不要變成發(fā)抖的小喵喵喵喵喵喵閱讀 3,820評(píng)論 0 15
  • simplify the life HTML5 File API — 讓前端操作文件變的可能 前言 在 HTML5...
    我是強(qiáng)強(qiáng)閱讀 3,313評(píng)論 0 1
  • 前端無(wú)法像原生APP一樣直接操作本地文件最仑,否則的話打開(kāi)個(gè)網(wǎng)頁(yè)就能把用戶電腦上的文件偷光了藐俺,所以需要通過(guò)用戶觸發(fā),用...
    雷波_viho閱讀 823評(píng)論 0 1
  • 功能需求 1.完成圖片的上傳和上傳之后圖片的預(yù)覽功能 遇到的問(wèn)題 1.使用html的src標(biāo)簽,可以直接發(fā)送請(qǐng)求泥彤。...
    張培_閱讀 3,927評(píng)論 0 3
  • 01 在南方的12月天氣是蠻冷的吟吝,昨天跟幾個(gè)朋友去吃火鍋了菱父。 然后吐槽了周邊的所見(jiàn)所聞然,順便聊到了關(guān)于朋友借錢(qián)不...
    余大凡閱讀 1,074評(píng)論 0 0