JavaScript實戰筆記(五) 預覽本地圖片

一般情況下,實現本地圖片預覽有兩種方法,一種是 DataURL,一種是 Blob

在正式開始介紹之前,我們先來了解一下什麼是 DataURL 和 Blob

1、DataURL

(1)介紹

DataURL 就是以 data: 開頭的 URL,它將數據編碼成特定的格式,並允許開發者在文檔中嵌入

(2)格式

data:[<mediatype>][;base64],<data>
  • data::前綴部分,DataURL 都必須以 data: 開頭

  • [<mediatype>]:表明數據的 MIME 類型,可選項,默認爲 text/plain;charset=US-ASCII

    MIME 的標準格式是 type/subtype,其中,type 表明數據的分類,subtype 表明具體的類型

    常見的 typesubtype 如下:

    • typetext ,表明數據是普通文本,例如 text/plaintext/html

      在DataURL 中,如果數據是普通文本,還可以在後面用 charset 指定編碼,例如 charset=UTF-8

    • typeimage,表明數據是圖像類型,例如 image/jpegimage/png

    • typeaudio,表明數據是音頻類型,例如 audio/mpegaudio/wav

    • typevideo,表明數據是視頻類型,例如 video/webmvideo/ogg

    • typeapplication,表明數據是二進制類型,例如 application/octet-stream

  • [;base64]:可選項,添加後表明後面的數據 <data> 是經過 Base64 編碼的

    如果數據是簡單文本,那麼可以直接嵌入,不需要經過 Base64 編碼,因此可以省略

    如果數據是其它類型,那麼必須加上 ;base64,並在後面嵌入以 Base64 編碼的數據

  • <data>:實際的數據,可以是普通文本,可以是經過 Base64 編碼的數據

    如果是經過 Base64 編碼的數據,那麼前面必須要有 ;base64

(3)例子

  • 普通文本
// 不指定文本編碼,顯示亂碼:浣犲ソ
data:text/plain,你好

// 指定文本編碼爲 UTF-8,顯示正確:你好
data:text/plain;charset=UTF-8,你好

// 指定文本編碼爲 UTF-8,並使用 Base64 編碼數據,顯示正確:你好
data:text/plain;charset=UTF-8;base64,5L2g5aW9
  • 其它類型,例如圖片
data:image/jpeg;base64,iVBORw0KGgoAAAANSUhE...

2、Base64

(1)介紹

Base64 是一種基於二進制的編碼協議

它的主要目的是用六十四個可打印字符表示所有數據(包括中文、二進制等)

在默認情況下,這六十四個可打印字符包括 [A-Z|a-z|0-9|+|/]

(2)原理

Base64 的編碼過程很簡單,下面我們直接用一個例子來進行講解

原數據:Hello,原數據二進制:01001000 01100101 01101100 01101100 01101111

1、將原數據,每 3 個字節分爲 1 組,這樣就能得到多組 24 個二進制位

(01001000 01100101 01101100) (01101100 01101111)

如果不足 24 個二進制位,後面會講如何處理

2、將每組 24 個二進制位,每 6 個二進制位分爲 1 個小組,這樣每個大組包含 4 個小組

(010010 000110 010101 101100) (011011 000110 1111)

如果不足 6 個二進制位,後面補 0;如果不足一組 6 個二進制位,用 = 代替

(010010 000110 010101 101100) (011011 000110 111100 =)

3、對每組 6 個二進制位,在前面添加兩個二進制位 00,這樣每個大組包含 4 個字節
   
(00010010 00000110 00010101 00101100) (00011011 00000110 00111100 =)

4、最後對照編碼表,查詢每個字節對應的編碼,就能得到原數據的 Base64 編碼

SGVsbG8=

實際上編碼表也很簡單,如下:

二進制字節 00000000 ~ 00011001 對應編碼 A ~ Z
二進制字節 00011010 ~ 00110011 對應編碼 a ~ z
二進制字節 00110100 ~ 00111101 對應編碼 0 ~ 9
二進制字節 00111110 對應編碼 +
二進制字節 00111111 對應編碼 /

(3)應用

Web API 中有編碼和解碼 Base64 數據的方法,分別是 btoa()atob()

  • btoa():編碼,對數據進行 Base64 編碼
  • atob():解碼,對經過 Base64 編碼後的數據解碼
let originalStr = 'Hello'
let encodedData = window.btoa(originalStr)
let decodedData = window.atob(encodedData)

console.log(encodedData) // SGVsbG8=
console.log(decodedData) // Hello

3、Blob

Blob 對象表示一個不可變、原始數據的類文件對象,構造函數如下:

Blob(array, options)
  • array:一個由 ArrayBufferBlobDOMString 等對象構成的 Array
  • options:對象類型,包含一些可選屬性,包括
    • type:表示 MIME 類型,默認爲 ""
    • endings:指定包含行結束符 \n 的字符串如何被寫入,默認爲 transparent

Blob 對象具有以下屬性:

  • size:只讀屬性,表示數據的大小
  • type:只讀屬性,表示數據的 MIME 類型

Blob 對象具有以下方法:

  • text():返回一個 Promise,包含 Blob 所有內容的 UTF-8 格式的 USVString
  • arrayBuffer():返回一個 Promise,包含 Blob 所有內容的 Binary 格式的 ArrayBuffer

4、File

File 對象提供有關文件的信息,它是一個特殊的 BlobFile 對象具有以下屬性:

  • size:只讀屬性,表示數據的大小
  • type:只讀屬性,表示數據的 MIME 類型
  • name:只讀屬性,表示文件的名稱
  • lastModified:只讀屬性,表示文件的最後修改時間,自 UNIX 時間起始值以來的毫秒數
  • lastModifiedDate:只讀屬性,表示文件的最後修改時間,Date 對象

5、DataURL 與 Blob 的轉化

(1)DataURL -> Blob

function dataURLtoBlob(dataURL) {
    return new Promise((resolve, reject) => {
        let [descString, dataString] = dataURL.split(',')
        let mimeType = descString.split('data:')[1].split(';base64')[0]
        let byteData = window
        	? window.atob(dataString)
        	: Buffer.from(dataString, 'base64').toString('binary')
        let intArray = new Uint8Array(new ArrayBuffer(byteData.length))
        intArray.map((val, idx, arr) => { arr[idx] = byteData.charCodeAt(idx) })
        let blob = new Blob([intArray], {type: mimeType})
        resolve(blob)
    })
}

(2)Blob -> DataURL

function blobToDataURL(blob) {
    return new Promise((resolve, reject) => {
        blob.arrayBuffer().then((arrayBuffer) => {
            let mimetype = blob.type
            let intArray = new Uint8Array(arrayBuffer)
            let byteData = [].map.call(intArray, (val) => {
                return String.fromCharCode(val)
            }).join('')
            let dataString = window
                ? window.btoa(byteData)
                : Buffer.from(byteData).toString('base64')
            let descString = 'data:' + mimetype + ';base64'
            let dataURL = descString + ',' + dataString
            resolve(dataURL)
        })
    })
}

注意,在上面的解決方案中,blob.arrayBuffer() 的兼容性其實並不是很好

事實上,我們有一個更加簡單的解決方案

function blobToDataURL(blob) {
    return new Promise((resolve, reject) => {
        let reader = new FileReader()
        reader.onloadend = function() {
            resolve(reader.result)
        }
        reader.readAsDataURL(blob)
    })
}

6、圖片預覽

(1)Blob + URL.createObjectURL()

<!DOCTYPE html>
<html>
<head>
    <script>
        function bindEvent() {
            const input = document.querySelector('input[type=file]')
            input.addEventListener('change', uoloadFile)
        }

        function uoloadFile(e) {
            let files = e.target.files;
            if (files.length > 0) {
                let file = files[0]
                document.querySelector('#avatar').src = URL.createObjectURL(file)
            }
        }
    </script>
</head>

<body onload="bindEvent()">
    <input type="file" accept="image/png, image/jpeg" />
    <image id="avatar" />
</body>

</html>

(2)DataURL

<!DOCTYPE html>
<html>
<head>
    <script>
        function bindEvent() {
            const input = document.querySelector('input[type=file]')
            input.addEventListener('change', uoloadFile)
        }

        function uoloadFile(e) {
            let files = e.target.files;
            if (files.length > 0) {
                let file = files[0]
                blobToDataURL(file).then((dataURL) => {
                    document.querySelector('#avatar').src = dataURL
                })
            }
        }

        function blobToDataURL(blob) {
            return new Promise((resolve, reject) => {
                let reader = new FileReader()
                reader.onloadend = function() {
                    resolve(reader.result)
                }
                reader.readAsDataURL(blob)
            })
        }
    </script>
</head>
<body onload="bindEvent()">
    <input type="file" accept="image/png, image/jpeg" />
    <image id="avatar" />
</body>
</html>

(3)實現效果

在這裏插入圖片描述

【 閱讀更多 JavaScript 系列文章,請看 JavaScript學習筆記

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章