LoginSignup
19
6

More than 3 years have passed since last update.

Javascript でバイナリデータを扱うまとめ

Last updated at Posted at 2021-01-02

ブラウザのjavascriptでバイナリデータを扱う方法まとめ。
以下ES6以上前提です。IEなどというブラウザはもう無い。

バイナリデータを扱うObject

ArrayBuffer

バイト列を扱う配列。メモリの連続領域に確保される。
そのままでは要素にアクセスできない。

const buf = new ArrayBuffer(length)
buf.byteLength //バイト長
buf.slice(ofs,len) //ofsからlenバイトの別ArrayBufferを返す

参照:ArrayBuffer MDN

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)
}

参照:TypedArray MDN

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)

Blob MDN

変換

文字列

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"
19
6
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19
6