JS二进制

JavaScript二进制数据的储存,读写操作,二进制文件对象

Posted by page on June 22, 2022

JS二进制

二进制

位(bit):计算机最小存储单位

字节(byte):8个位组成,可记录字母数字和符号;如ASCII(1个字节英文),Unicode(2个字节支持所有语言,由此衍生utf-8),

字:计算机的最小运算单位;32位计算机:1字=32位=4字节,64位计算机:1字=64位=8字节

数字转换

num.toString(n) 十进制数字转 n 进制

parseInt(num, n) 将 n 进制数字转为十进制数

ArrayBuffer

字节数组:表示储存二进制数据的一段内存,创建ArrayBuffer对象为指定长度连续内存提供容器;

// 开辟16字节长度内存引用为1个buffer
const buffer1 = new ArrayBuffer(16); // 默认以二进制0填充全部位

不可被操作,在其他语言中名为bytearray

TypedArray

类型数组:将ArrayBuffer实例这一内存上字节按照9种类型规则之一读取为数字

const buffer1 = new ArrayBuffer(16);
const uint8 = new Uint8Array(buffer1); // 每8位为1个整数,即1数字占1字节,此时长度为2
const uint16 = new Uint16Array(buffer1); // 每16位为1个整数,即1数字占2字节,此时长度为1
const uint32 = new Uint32Array(buffer1); // 每32位为1个整数,即1数字占4字节
const float64 = new Float64Array(buffer1); // 每64位为1个浮点数,即1数字占8字节
console.log(float64.buffer);
// ...有符号整数 无符号整数 浮点数

DataView

自定义视图:从二进制ArrayBuffer对象中读写多种数值类型的底层接口,由如何读与如何写两类方法实现;

const buffer1 = new ArrayBuffer(16);
const dataView1 = new DataView(buffer1);
dataView1.setInt8(0, 1); // 以8位作为1个整数的方式在第0个整数写入数值1
console.log(dataView1.getUint8(0)); // 以8位作为1个整数的方式读取第0个整数
console.log(dataView1.getUint16(0)); // 以16位作为1个整数的方式读取第0个整数
console.log(dataView1.buffer);
....

Blob

Blob 对象表示一个不可变、原始数据的类文件对象(即一个可用的原数据);

// array: ArrayBuffer, ArrayBufferView, Blob/File, String类型组成的数组
// options: type 为 文件内容的MIME类型 
const blob = new Blob(array, options);
// 属性 size单位为位(byte)
console.log(blob.size, blob.type);
// 方法
slice()  Blob 中截取到指定字节位置并返回一个新的 Blob(用法同数组的 slice)
arrayBuffer() 返回一个以二进制形式展现的 promise
stream() 返回一个ReadableStream对象
text() 返回一个文本形式的 promise

Blob概念来自数据库,web中Blob对象并不是JS内置的,更像是为实际应用而提供的Web API形式产物;

文件对象是一个可用数据的完整体,关注宏观的;

相比较下ArrayBuffer是一段内存上二级制数据,底层的,关注微观的;

Blob附带有文件信息(mime-type),作为一个文件二进制数据整体,适合用于文件传输;

而需要关注细节(如要修改某一段数据时),使用ArrayBuffer比较好。

流:流本身只有目标资源的指向与如何操作目标资源(在流被创建时已确定),对于使用者仅关注如何读取与写入;

流媒体:媒体数据压缩后分段传输,无需下载整个文件,实现各种文件类型的实时传输;

Blob可转换为ReadableStream后支持数据操作;

blob url

fileBlob类型的对象保存在当前操作的document下,存储在内存中。转为UTF-16的字符串作为资源标识符;

URL.createObjectURL(file/blob):生成blob

URL.revokeObjectURL(objectURL):手动清除

// 前端下载
export const downloadFile = async (params, fileName) => {
  const results = await download(params); // Blob
  const a = document.createElement("a");
  a.download = fileName + ".xlsx";
  // 生成blob url
  a.href = window.URL.createObjectURL(results);
  a.style.display = "none";
  document.body.appendChild(a);
  a.click();
  // 释放内存
  window.URL.revokeObjectURL(a.href);
  document.body.removeChild(a);
};

File

描述文件信息的JS内置对象,是JavaScript直接访问文件的接口;继承于Blob;

// array: ArrayBuffer, ArrayBufferView, Blob, String类型组成的数组
// options: type 为 文件内容的MIME类型 
const file = new File(array, name, options);
const file = new File([blob], name, { type: blob.type })

web应用中通过type为’file’的表单按钮选中的客户端文件即以File实例表现;

const file1 = new File(["文件对象"], "test", { type: "text/plain" });
// 除了继承自Blob的属性/方法外,子类上有
lastModified: 1640589621358 // 上次修改时间戳
lastModifiedDate: Mon Dec 27 2021 15:20:21 GMT+0800 (中国标准时间) 上次修改日期对象
name: "test" // 文件名

分片上传

const chunkSize = 30000; // 分片size
let start = 0, curFile, isPause = false;
const url = "https://httpbin.org/post";
async function upload(){
    const file = curFile;
    for(start; start < file.size; start += chunkSize){
        if(isPause) break;
        const chunk = file.slice(start, start + chunkSize + 1);
        const fd = new FormData();
        fd.append("data", chunk);
        await fetch(url, { method: "post", body: fd }).then((res) =>{
                res.text()
            }
        );
        if(chunk.size < chunkSize){
            uploadSuccess();
            return;
        }
    }
}
function pause(){
    isPause = true;
}
function continues(){
    isPause = false;
    upload();
}
function uploadSuccess(){
    isPause = false;
    start = 0;
}

以上只是示例,真实情况可能需要传递其它参数:id(上传文件标识id),startByte/endByte/totalSize(分片/文件信息)

Base64

二进制数据(比如图片)转化为可打印字符以此传输数据

对数据简单的加密,肉眼是安全的

html或者css支持处理base64图片,可以减少http请求

// 编码
const base64 = window.btoa("hello randy");
console.log(base64); // aGVsbG8gcmFuZHk=
// 解码
const str = window.atob(base64);
str = base64.substring(base64.indexOf(',') + 1); // base64报错尝试

压缩图片

借助canvas.toDataURL(mimeType, quality)canvas.toBlob(callback, mimeType, quality)返回压缩后base64/blob;

// compress.js
const MAX_WIDTH = 800; // 图片最大宽度
// 图片压缩方法
function compress(base64, quality, mimeType) {
    let canvas = document.createElement("canvas");
    let img = document.createElement("img");
    img.crossOrigin = "anonymous";
    return new Promise((resolve, reject) => {
        img.src = base64;
        img.onload = () => {
            let targetWidth, targetHeight;
            if (img.width > MAX_WIDTH) {
                targetWidth = MAX_WIDTH;
                targetHeight = (img.height * MAX_WIDTH) / img.width;
            } else {
                targetWidth = img.width;
                targetHeight = img.height;
            }
            canvas.width = targetWidth;
            canvas.height = targetHeight;
            let ctx = canvas.getContext("2d");
            ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
            let imageData = canvas.toDataURL(mimeType, quality / 100); // 设置图片质量
            resolve(imageData);
            let canvas = null;
            let img = null;
        };
    });
}

// 转换为 Blob 对象 用于数据传输
function dataUrlToBlob(base64, mimeType) {
    let bytes = window.atob(base64.split(",")[1]);
    let ab = new ArrayBuffer(bytes.length);
    let ia = new Uint8Array(ab);
    for (let i = 0; i < bytes.length; i++) {
        ia[i] = bytes.charCodeAt(i);
    }
    return new Blob([ab], { type: mimeType });
}

function loadFile(event) {
    const reader = new FileReader();
    reader.onload = async function () {
        let compressedDataURL = await compress(
            reader.result,
            90,
            "image/jpeg"
        );
        let compressedImageBlob = dataUrlToBlob(compressedDataURL);
        uploadFile("https://httpbin.org/post", compressedImageBlob);
    };
    reader.readAsDataURL(event.target.files[0]);
};

FileReader

允许 Web 应用程序异步读取存储在用户计算机上的文件或原始数据缓冲区为指定数据格式内容

const blob = new Blob(["hello", "randy"], { type: "text/plain" });
const fileReader = new FileReader();
// 方法:readAsArrayBuffer readAsBinaryString readAsDataURL readAsText abort
fileReader.readAsDataURL(blob);
// 事件:onabort onload onloadstart onloadend onprogress
fileReader.onload = () => {
  console.log(fileReader);
  // 属性:readyState 0 还没有加载任何数据 1 数据正在被加载 2 已完成全部的读取请求
  // fileReader.result 结果(如果成功)
  // fileReader.error  error(如果失败)。
};

获取图片分辨率

let resolution = [];
const fileReader = new FileReader();
fileReader.onload = () => {
  if (fileReader.error) return;
  let $img = new Image();
  $img.onload = () => {
    resolution = [$img.width, $img.height];
    $img = null;
  };
  $img.onerror = () => {
    resolution = [];
    $img = null;
  };
  $img.src = fileReader.result;
};
fileReader.readAsDataURL(file.raw);

获取视频分辨率

let resolution = [];  // 分辨率
let duration = 0;  // 视频时长
const fileReader = new FileReader();
fileReader.onload = () => {
  if (fileReader.error) return;
  let $video = document.createElement('video');
  $video.onloadeddata = () => {
    resolution = [$video.videoWidth, $video.videoHeight];
    duration = parseInt($video.duration);
    $video = null;
  };
  $video.stalled = () => {
    resolution = [];
    $video = null;
  };
  $video.src = fileReader.result;
};
fileReader.readAsDataURL(file.raw);

类型转换

File与Blob可直接相互转换,File/Blob可通过FileReader与Base64转换

// base64转blob
var byteString = window.atob(dataURI.split(",")[1]);
var mimeString = dataURI
  .split(",")[0]
  .split(":")[1]
  .split(";")[0];
var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < byteString.length; i++) {
  ia[i] = byteString.charCodeAt(i);
}
return new Blob([ab], { type: mimeString });

img文件转base64:

  • 通过请求获取到img的blob/ArrayBuffer/BinaryString类型数据经FileReader.readAsDataURL转换

  • img绘制至canvas,canvas.toDataURL(mime/type)

blob-util 第三方转换插件,兼容性优良;