Web编码和流

原创
07/20 14:11
阅读数 6

计算机中的数据,底层实现都是依靠二进制符号,即0和1。这些数据可以表示一个文件,比如图片、音乐、视频,或是一个字符串。它们都是二进制符号,但由于编解码方式的不同,展现的形态和使用的方式也不同。

比如,PNG格式图片以一定方式重新编码了点阵图像数据,达到了压缩的目的;UTF-8以1个字节为一个单位,以单个或多个单位来让二进制符号表示文字。

举个例子,在一次Http请求中包含了头信息:Content-Type:'text/plain;charset=UTF-8,并获取到了数据:

01001000 01100001 01100011 01101011 00100000 01110100 01101000 01100101 00100000 01010111 01101111 01110010 01101100 01000100

那么计算机就知道,这一段数据是一个按UTF-8编码的字符串,按照UTF-8的编解码规则,将这段数据解码为:

Hack the World

但如果非字符串的数据,按照字符串来解码,那么结果将是乱码。

编码

在Javascript中,常用的表示二进制的类有ArrayBufferBlob

ArrayBuffer

ArrayBuffer类用来存储二进制数据,它不能直接操作,需要通过类型数组来操作。也就是说ArrayBuffer只是“容器”,而要操作这些数据,需要将其转换为某种形式,比如(Uint8Array和DataView)。

类型化数组的基类是TypedArray,不能直接使用,我们需要使用它的子类:Int8Array,Uint8Array,Uint8ClampedArray,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array。

示例:

let buffer = new ArrayBuffer(8) // 8字节数据
let view = new Int32Array(buffer) // 32位整数,4个字节表示一个数字,所以view数组有2个元素
view[0] = 0
view[1] = 1

这个示例中,我们创建了一个8字节的ArrayBuffer,其中包含了64 bit的数据。接着我们用Int32Array来“识别”这些二进制数据,也就是是说从头开始,每32个二进制符被认为是一个整数。

Blob

Blob类表示二进制数据,通常是文件(比如图片、音频、视频)。它除了保存数据,还保存了数据的MIME类型。

示例:

let data = ['<html><head><title>Hack the World</title></head><body></body></html>']
let blob = new Blob(myBlobParts, { type: 'text/html' })

Blob是不可变对象类。它有2个只读属性:

  • size:数据大小,以字节为单位。
  • type:字符串,MIME。

有4个方法:

  • slice:返回一个新的Blob对象,包含了源Blob对象中指定范围内的数据。
  • stream:返回一个能读取Blob内容的 ReadableStream。
  • text:返回一个Promise对象且包含blob所有内容的UTF-8格式的USVString。
  • arrayBuffer:返回一个Promise对象且包含blo所有内容的二进制格式的ArrayBuffer。

举个例子,我们通过接口获取图片时,获得ArrayBuffer格式的二进制数据,转换成Blob对象,再转换为base64以赋值给img标签的src属性来预览。

fetch('http://trace-wings.oss-cn-hangzhou.aliyuncs.com/resource/image/avatar.png')
  .then(res => res.arrayBuffer())
  .then(buffer => {
    let blob = new Blob([buffer])
    let url = URL.createObjectURL(blob)

    image.src = url
  })

Base64

Base64是前端常用的编码格式,特别是在表示图片时。img标签的src属性可以接受一个URL地址,也可以接受一个Base64字符串。

Base64是一种基于64个可打印字符来表示二进制数据的表示方法,它将6个二进制符作为一组,用一个字符表示,编码为一个字符串。也就是用字符串来表示二进制数据,但这个字符串是没有什么实际意义的。

它通常是这样的格式:

data:[<mediatype>][;base64],<data>

其中<mediatype>表示MIME类型,<data>表示数据。

除了上面的例子外,前端还常将input上传的图片用Base64来表示以预览:

let reader = new FileReader()
reader.onload = function () {
  image.src = reader.result
}
reader.readAsDataURL(event.target.files[0])

上传的文件event.target.files[0]是一个浏览器底层数据结构(特殊类型的Blob),FileReader可以读取它并转换为Base64。

btoa和atob

DOM中有两个工具函数:btoaatob,用于将字符串转换为Base64字符串和将Base64字符串转换为字符串。

示例:

let a = btoa('Hack the World') // SGFjayB0aGUgV29ybGQ=
let b = atob(a) // Hack the World

这两个函数的本质是将二进制数据在Unicode和Base64两种不同的编码方式之间转换。

XMLHttpRequest

XMLHttpRequest可以用于发起一个异步请求,默认情况下获得的数据格式是字符串。我们可以通过修改responseType属性来获得不同类型的数据。

比如,异步获取一张图片Blob:

let xhr = new XMLHttpRequest()
xhr.open('GET', 'http://localhost:8000')
xhr.responseType = 'blob'
xhr.send()
xhr.onload = () => {
  xhr.response // Blob
}

Blob会根据Content-Type响应头来设置文件类型。

所有可以获得的类型有:

  • arraybuffer:ArrayBuffer对象
  • blob:Blob对象
  • document:文档
  • json:普通对象
  • text:字符串

fetch

fetch也是发起异步请求的函数,返回结果主体是一个ReadableStream,其提供了

  • arrayBuffer
  • blob
  • json
  • text

方法来将响应主体转换为相应的格式。

node

客户端创建的流的源头来自于服务端的流。

比如node中:

import * as Http from 'http'
import Fs from 'fs'

Http.createServer(async (req, res) => {
  let stream = Fs.createReadStream('./avatar.png')

  res.setHeader('Access-Control-Allow-Origin', '*')
  res.setHeader('Access-Control-Allow-Headers', '*')
  res.setHeader('Content-Type', 'image/png')

  stream.pipe(res)
}).listen(8000)

方法createReadStream创建了一个流,并导向响应。浏览器会自动处理流,流接收完毕后触发请求完毕的事件。

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部