欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

JavaScript实战笔记(五) 预览本地图片

程序员文章站 2022-04-28 21:27:46
...

一般情况下,实现本地图片预览有两种方法,一种是 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 系列文章,请看 JavaScript学习笔记