ブラウザのjavascriptでバイナリデータを扱う方法まとめ。
以下ES6以上前提です。IEなどというブラウザはもう無い。
##バイナリデータを扱うObject
ArrayBuffer
バイト列を扱う配列。メモリの連続領域に確保される。
そのままでは要素にアクセスできない。
const buf = new ArrayBuffer(length)
buf.byteLength //バイト長
buf.slice(ofs,len) //ofsからlenバイトの別ArrayBufferを返す
DataView
ArrayBufferを操作するためのビュー。エンディアンを指定して数値データとして操作できるset/getメソッドが各種用意されている。
const buffer = new ArrayBuffer(8)
const dataview = new DataView(buffer)
dataview.setInt16(byteOffset, value [, littleEndian])
dataview.getInt16(byteOffset [, littleEndian])
参照:DataView MDN
TypedArray
全ての要素が同じ型の、ArrayBufferに対するビュー。
以下の種類がある。
Int8Array Uint8Array Uint8ClampedArray
Int16Array Uint16Array
Int32Array Uint32Array
Float32Array Float64Array
BigInt64Array BigUint64Array
Uint8ClampedArrayは、0未満は0に、256以上は255に丸める。
コンストラクタに様々な形式をとって初期化できる。
new TypedArray() //空の配列
new TypedArray(length) //サイズ指定0初期化
new TypedArray(typedArray) //他のTypedArrayからの変換
new TypedArray(object) //iterableなobjectからの変換
new TypedArray(buffer [, byteOffset [, length]]) //ArrayBufferのビューとして生成(実体はコピーされない)
[index]で要素にアクセスできる。
forEach,map等のArrayと同様のメソッドが使える。
ただし、push,popのような要素数の増減はできない。
bufferプロパティでArrayBufferとして参照できる。
部分をコピーして取り出すときはslice。
別なTypedArrayにコピーするときはset methodが高速。
function memcpy(dst, dstOffset, src, srcOffset, length) {
var dstU8 = new Uint8Array(dst, dstOffset, length)
var srcU8 = new Uint8Array(src, srcOffset, length)
dstU8.set(srcU8)
}
Blob
blobはファイルを抽象化したインターフェイス。中身をArrayBufferとして非同期的に取り出せ、ファイルタイプ(mimetype)を指定することで、ファイルの形式を指定することができる。
コンストラクタ。配列で渡されたArrayBufferを結合する。
const newBlob = new Blob([buffer], {type:"mimetype"})
blobの中身を取り出すのは、FileReaderを使う。
const fr = new FileReader()
fr.onloadend = function() {
fr.result //ArrayBuffer
}
fr.readAsArrayBuffer(newBlob)
変換
文字列
ArrayBufferから文字列を取り出す。
ASCIIの場合、配列にバラして1文字づつfromCharCodeする。
let str = Array.from(buf.slice(ofs,size), e => String.fromCharCode(e)).join("")
Javascriptの文字列は内部表現がUTF-16なので、上の方法ではUTF-8のマルチバイト文字が化ける。UTF-8文字列を変換するには以下のようにいったん%u形式にURIEncodeしてからdecodeする。
function arrayBufferUTF8ToString(arrayBuffer) {
const escape = (str) => {
return str.replace(/[^a-zA-Z0-9@*_+\-./]/g, function(m) {
const code = m.charCodeAt(0);
if (code <= 0xff) {
return '%' + ('00' + code.toString(16)).slice(-2).toUpperCase()
} else {
return '%u' + ('0000' + code.toString(16)).slice(-4).toUpperCase()
}
})
}
const uint8Array = new Uint8Array(arrayBuffer)
const binStr = String.fromCharCode(...uint8Array)
return decodeURIComponent(escape(binStr))
}
逆に文字列をUTF-8としてArrayBufferに格納する場合。
Blobを使ってUTF-8に変換できる。
function str2bytes(str) {
return new Promise((resolve,reject)=>{
const fr = new FileReader()
fr.onloadend = function() {
resolve(fr.result)
}
fr.readAsArrayBuffer(new Blob([str]))
}
}
画像
ArrayBufferの画像ファイルデータを画像として表示。
Blobにfile typeを指定して取り込み、それをcreateObjectURLでdeta:uriに変換。
const imgblob = new Blob([buffer],{type:"image/jpeg"})
const img = new document.createElement("img")
img.src = URL.createObjectURL(imgblob)
非圧縮のRGBAデータをArrayBufferで作ってcanvasで扱う場合。
ImageDataで縦横サイズを指定して、canvasに書き込む。
const buffer = new ArrayBuffer(width*height*4)
const imageData = new ImageData(buffer, width , height)
ctx.putImageData(imageData,0, 0)
読み書き
urlから読み込み
XHRを使ってArrayBufferに読み込み、responseTypeを"arraybuffer"にする。
const req = new XMLHttpRequest()
req.open("get",url,true)
req.responseType = "arraybuffer"
req.onload = () => {
if(req.status==200) {
req.response //ArrayBuffer
} else {
alert("file cannot load")
}
}
req.send()
local fileから読み込み
input type=fileでユーザが指定したファイルから、FileReaderを使って読みこむ。
const files = document.getElementById("fileinput").files
const reader = new FileReader()
reader.onload = (ev) =>{
reader.result //ArrayBuffer
}
reader.readAsArrayBuffer(files[0])
ファイルとしてダウンロード
Blobでファイルタイプを指定してから、createObjectURLでURIに変換して、リンクからダウンロードさせる
const datablob = new Blob([buffer],{type:"application/octet-stream"})
const a = document.getElementById("downloadlink")
a.href = URL.createObjectURL(datablob)
a.download = "filename"