ArrayBuffer
コンストラクタArrayBuffer
メソッドArrayBuffer.prototype
プロパティTypedArray
メソッドTypedArray.prototype
プロパティ«ElementType»Array
コンストラクタ«ElementType»Array
プロパティ«ElementType»Array.prototype
プロパティDataView
コンストラクタDataView.prototype
プロパティXMLHttpRequest
型付き配列は、バイナリデータを扱うためのECMAScript 6 APIです。
コード例
const
typedArray
=
new
Uint8Array
([
0
,
1
,
2
]);
console
.
log
(
typedArray
.
length
);
// 3
typedArray
[
0
]
=
5
;
const
normalArray
=
[...
typedArray
];
// [5,1,2]
// The elements are stored in typedArray.buffer.
// Get a different view on the same data:
const
dataView
=
new
DataView
(
typedArray
.
buffer
);
console
.
log
(
dataView
.
getUint8
(
0
));
// 5
ArrayBuffer
のインスタンスは、処理されるバイナリデータを格納します。データにアクセスするには、2種類のビューが使用されます。
Uint8Array
、Int16Array
、Float32Array
など)は、ArrayBufferを単一の種類の要素のインデックス付きシーケンスとして解釈します。DataView
のインスタンスを使用すると、ArrayBuffer内の任意のバイトオフセットで、複数の種類(Uint8
、Int16
、Float32
など)の要素としてデータにアクセスできます。以下のブラウザAPIは型付き配列をサポートしています(詳細は専用のセクションで説明されています)。
ウェブ上で遭遇するデータの多くはテキストです。JSONファイル、HTMLファイル、CSSファイル、JavaScriptコードなどです。このようなデータを処理するには、JavaScriptの組み込み文字列データ型がうまく機能します。しかし、数年前までは、JavaScriptはバイナリデータを処理するのに不向きでした。2011年2月8日、型付き配列仕様1.0はバイナリデータを処理するための機能を標準化しました。現在では、型付き配列はさまざまなエンジンで広くサポートされています。ECMAScript 6では、コア言語の一部になり、以前は配列(map()
、filter()
など)に対してのみ利用可能だった多くのメソッドが追加されました。
型付き配列の主なユースケースは次のとおりです。
型付き配列APIでは、2種類のオブジェクトが連携して動作します。
ArrayBuffer
のインスタンスはバイナリデータを保持します。Uint8Array
、Float64Array
など)のインスタンスは、通常の配列と非常によく似ていますが、要素には単一の種類しか許容せず、穴はありません。DataView
のインスタンスを使用すると、バッファ内の任意のバイトオフセットのデータにアクセスし、そのデータをいくつかの種類(Uint8
、Float64
など)のいずれかとして解釈できます。これは、型付き配列APIの構造図です(注目点:すべての型付き配列には共通のスーパークラスがあります)。
APIでは、次の要素の種類がサポートされています。
要素の種類 | バイト数 | 説明 | C言語の型 |
---|---|---|---|
Int8 | 1 | 8ビット符号付き整数 | signed char |
Uint8 | 1 | 8ビット符号なし整数 | unsigned char |
Uint8C | 1 | 8ビット符号なし整数(クランプ変換) | unsigned char |
Int16 | 2 | 16ビット符号付き整数 | short |
Uint16 | 2 | 16ビット符号なし整数 | unsigned short |
Int32 | 4 | 32ビット符号付き整数 | int |
Uint32 | 4 | 32ビット符号なし整数 | unsigned int |
Float32 | 4 | 32ビット浮動小数点数 | float |
Float64 | 8 | 64ビット浮動小数点数 | double |
要素の種類Uint8C
は特殊です。DataView
ではサポートされておらず、Uint8ClampedArray
を有効にするためだけに存在します。この型付き配列は、canvas
要素で使用されます(ここでCanvasPixelArray
に取って代わります)。Uint8C
とUint8
の違いは、オーバーフローとアンダーフローの処理方法だけです(次のセクションで説明します)。前者を使用しないことをお勧めします。-Brendan Eichの引用
念のため、(そして私が生まれたときのことを知っているから)、
Uint8ClampedArray
は完全に歴史的な遺物です(HTML5 canvas要素の)。canvas関連の作業を本当にしている場合を除き、避けてください。
通常、値が要素の種類の範囲外にある場合、剰余演算を使用して範囲内の値に変換します。符号付き整数と符号なし整数の場合は、次のようになります。
符号なし8ビット整数の剰余変換
> const uint8 = new Uint8Array(1);
> uint8[0] = 255; uint8[0] // highest value within range
255
> uint8[0] = 256; uint8[0] // overflow
0
> uint8[0] = 0; uint8[0] // lowest value within range
0
> uint8[0] = -1; uint8[0] // underflow
255
符号付き8ビット整数の剰余変換
> const int8 = new Int8Array(1);
> int8[0] = 127; int8[0] // highest value within range
127
> int8[0] = 128; int8[0] // overflow
-128
> int8[0] = -128; int8[0] // lowest value within range
-128
> int8[0] = -129; int8[0] // underflow
127
クランプ変換は異なります。
> const uint8c = new Uint8ClampedArray(1);
> uint8c[0] = 255; uint8c[0] // highest value within range
255
> uint8c[0] = 256; uint8c[0] // overflow
255
> uint8c[0] = 0; uint8c[0] // lowest value within range
0
> uint8c[0] = -1; uint8c[0] // underflow
0
型(Uint16
など)が複数のバイトとして格納される場合、エンディアンが重要になります。
Uint16
値0xABCDは、2バイト(最初に0xAB、次に0xCD)として格納されます。Uint16
値0xABCDは、2バイト(最初に0xCD、次に0xAB)として格納されます。エンディアンは、通常、CPUアーキテクチャごとに固定され、ネイティブAPI全体で一貫しています。型付き配列はこれらのAPIと通信するために使用されるため、エンディアンはプラットフォームのエンディアンに従い、変更できません。
一方、プロトコルとバイナリファイルのエンディアンは異なり、プラットフォーム全体で固定されています。そのため、どちらのエンディアンでもデータにアクセスできる必要があります。DataViewは、このユースケースに対応し、値を取得または設定する際にエンディアンを指定できます。
プラットフォームのエンディアンを判別するには、次の関数を使用できます。
const
BIG_ENDIAN
=
Symbol
(
'BIG_ENDIAN'
);
const
LITTLE_ENDIAN
=
Symbol
(
'LITTLE_ENDIAN'
);
function
getPlatformEndianness
()
{
const
arr32
=
Uint32Array
.
of
(
0x12345678
);
const
arr8
=
new
Uint8Array
(
arr32
.
buffer
);
switch
((
arr8
[
0
]
*
0x1000000
)
+
(
arr8
[
1
]
*
0x10000
)
+
(
arr8
[
2
]
*
0x100
)
+
(
arr8
\
[
3
]))
{
case
0x12345678
:
return
BIG_ENDIAN
;
case
0x78563412
:
return
LITTLE_ENDIAN
;
default
:
throw
new
Error
(
'Unknown endianness'
);
}
}
ワード(バイトのペア)を、ワード内のバイトとは異なるエンディアンで配置するプラットフォームもあります。これは、混合エンディアンと呼ばれます。そのようなプラットフォームをサポートする必要がある場合は、前のコードを簡単に拡張できます。
ブラケット演算子[ ]
では、非負のインデックス(0から始まる)のみを使用できます。ArrayBuffer、型付き配列、DataViewのメソッドは異なります。すべてのインデックスが負になる可能性があります。負の場合は、長さから逆算されます。言い換えると、長さに加算されて通常のインデックスが生成されます。したがって、-1
は最後の要素を、-2
は最後から2番目の要素を参照します。通常の配列のメソッドも同様に動作します。
> const ui8 = Uint8Array.of(0, 1, 2);
> ui8.slice(-1)
Uint8Array [ 2 ]
一方、オフセットは非負でなければなりません。たとえば、次のように-1
を渡すと
DataView
.
prototype
.
getInt8
(
byteOffset
)
RangeError
が発生します。
ArrayBufferはデータを格納し、ビュー(型付き配列とDataView)を使用して読み書きします。DataViewを作成するには、コンストラクタにArrayBufferを提供する必要があります。型付き配列コンストラクタは、必要に応じてArrayBufferを作成することもできます。
ArrayBuffer
コンストラクタ コンストラクタのシグネチャは次のとおりです。
ArrayBuffer
(
length
:
number
)
new
を使用してこのコンストラクタを呼び出すと、容量がlength
バイトのインスタンスが作成されます。これらのバイトのそれぞれは、最初は0です。
ArrayBuffer
メソッド ArrayBuffer.isView(arg)
arg
がオブジェクトであり、ArrayBufferのビューである場合、true
を返します。型付き配列とDataViewのみが必要な内部スロット[[ViewedArrayBuffer]]
を持ちます。つまり、このチェックは、arg
が型付き配列またはDataView
のインスタンスであるかどうかをチェックすることとほぼ同等です。ArrayBuffer.prototype
プロパティ get ArrayBuffer.prototype.byteLength
ArrayBuffer.prototype.slice(start, end)
start
以上end
未満です。start
とend
は負にすることができます(「負のインデックス」を参照)。様々な種類の Typed Array は、要素の型だけが異なります。
Int8Array
, Uint8Array
, Uint8ClampedArray
, Int16Array
, Uint16Array
, Int32Array
, Uint32Array
Float32Array
, Float64Array
Typed Array は通常の Array と非常によく似ています。`length` プロパティを持ち、ブラケット演算子 `[ ]` を使用して要素にアクセスでき、標準的な Array メソッドもすべて備えています。Array との違いは以下の通りです。
new Array(10)
は要素のない通常の Array を作成します(穴だけがあります)。new Uint8Array(10)
は、10 個の要素がすべて 0 である Typed Array を作成します。Typed Array はキーが `Symbol.iterator` であるメソッドを実装しており、したがって反復可能です(詳細は「反復可能オブジェクトとイテレータ」を参照)。つまり、ES6 の `for-of` ループや同様のメカニズムを使用できます。
const
ui8
=
Uint8Array
.
of
(
0
,
1
,
2
);
for
(
const
byte
of
ui8
)
{
console
.
log
(
byte
);
}
// Output:
// 0
// 1
// 2
ArrayBuffer と DataView は反復可能ではありません。
通常の Array を Typed Array に変換するには、それを Typed Array コンストラクタのパラメータにします。例:
> const tarr = new Uint8Array([0,1,2]);
Typed Array を Array に変換する従来の方法は、`Array.prototype.slice` を呼び出すことです。この方法は、すべての Array-like オブジェクト(`arguments` など)と Typed Array に有効です。
> Array.prototype.slice.call(tarr)
[ 0, 1, 2 ]
ES6 では、Typed Array は反復可能なので、スプレッド演算子(`...`)を使用できます。
> [...tarr]
[ 0, 1, 2 ]
ES6 のもう一つの選択肢は `Array.from()` であり、これは反復可能オブジェクトと Array-like オブジェクトの両方で動作します。
> Array.from(tarr)
[ 0, 1, 2 ]
いくつかのメソッドは、`this` と同様の新しいインスタンスを作成します。Species パターンを使用すると、どのコンストラクタを使用するかを設定できます。例えば、`Array` のサブクラス `MyArray` を作成した場合、デフォルトでは `map()` は `MyArray` のインスタンスを作成します。`Array` のインスタンスを作成したい場合は、Species パターンを使用してそれを実現できます。詳細は、クラスに関する章の「Species パターン」を参照してください。
ArrayBuffer は以下の場所で Species パターンを使用します。
ArrayBuffer.prototype.slice()
Typed Array は以下の場所で Species パターンを使用します。
TypedArray<T>.prototype.filter()
TypedArray<T>.prototype.map()
TypedArray<T>.prototype.slice()
TypedArray<T>.prototype.subarray()
DataView は Species パターンを使用しません。
この章の冒頭の図にあるように、すべての Typed Array クラス(`Uint8Array` など)には共通のスーパー クラスがあります。そのスーパー クラスを `TypedArray` と呼んでいますが、JavaScript から直接アクセスすることはできません(ES6 仕様では、組込みオブジェクト `%TypedArray%` と呼ばれています)。`TypedArray.prototype` には、Typed Array のすべてのメソッドが含まれています。
静的 `TypedArray` メソッドは、そのサブクラス(`Uint8Array` など)によって継承されます。
TypedArray.of()
このメソッドのシグネチャは次のとおりです。
TypedArray
.
of
(...
items
)
`of()` が呼び出されたクラス(this)のインスタンスである新しい Typed Array を作成します。そのインスタンスの要素は、`of()` のパラメータです。
`of()` は Typed Array のカスタムリテラルとして考えることができます。
> Float32Array.of(0.151, -8, 3.7)
Float32Array [ 0.151, -8, 3.7 ]
TypedArray.from()
このメソッドのシグネチャは次のとおりです。
TypedArray
<
U
>
.
from
(
source
:
Iterable
<
T
>
,
mapfn
?
:
T
=>
U
,
thisArg
?
)
反復可能な `source` を `this`(Typed Array)のインスタンスに変換します。
例えば、通常の Array は反復可能であり、このメソッドで変換できます。
> Uint16Array.from([0, 1, 2])
Uint16Array [ 0, 1, 2 ]
Typed Array も反復可能です。
> const ui16 = Uint16Array.from(Uint8Array.of(0, 1, 2));
> ui16 instanceof Uint16Array
true
オプションの `mapfn` を使用すると、結果の要素になる前に `source` の要素を変換できます。なぜ *マッピング* と *変換* の 2 つのステップを一度に行うのでしょうか? `source.map()` を使用して最初のステップを個別に実行する場合と比較して、2 つの利点があります。
2 番目の利点を説明するために、`map()` を使用して Typed Array の要素を 2 倍にします。
> Int8Array.of(127, 126, 125).map(x => 2 * x)
Int8Array [ -2, -4, -6 ]
ご覧のように、値がオーバーフローし、`Int8` の値の範囲に強制変換されます。`from()` を使用してマップすると、値がオーバーフローしないように結果の型を選択できます。
> Int16Array.from(Int8Array.of(127, 126, 125), x => 2 * x)
Int16Array [ 254, 252, 250 ]
Allen Wirfs-Brock 氏によると、Typed Array 間のマッピングが `from()` の `mapfn` パラメータの動機でした。
TypedArray.prototype
のプロパティ Typed Array メソッドで受け入れられるインデックスは負の数にすることができます(従来の Array メソッドと同様に機能します)。オフセットは非負でなければなりません。詳細は、「負のインデックス」を参照してください。
次のプロパティは Typed Array に固有であり、通常の Array には存在しません。
get TypedArray<T>.prototype.buffer : ArrayBuffer
get TypedArray<T>.prototype.byteLength : number
get TypedArray<T>.prototype.byteOffset : number
TypedArray<T>.prototype.set(arrayOrTypedArray, offset=0) : void
TypedArray<T>.prototype.subarray(begin=0, end=this.length) : TypedArray<T>
次のメソッドは、基本的に通常の Array のメソッドと同じです。
TypedArray<T>.prototype.copyWithin(target : number, start : number, end = this.length) : This
TypedArray<T>.prototype.entries() : Iterable<[number,T]>
TypedArray<T>.prototype.every(callbackfn, thisArg?)
TypedArray<T>.prototype.fill(value, start=0, end=this.length) : void
TypedArray<T>.prototype.filter(callbackfn, thisArg?) : TypedArray<T>
TypedArray<T>.prototype.find(predicate : T => boolean, thisArg?) : T
TypedArray<T>.prototype.findIndex(predicate : T => boolean, thisArg?) : number
TypedArray<T>.prototype.forEach(callbackfn, thisArg?) : void
TypedArray<T>.prototype.indexOf(searchElement, fromIndex=0) : number
TypedArray<T>.prototype.join(separator : string = ',') : string
TypedArray<T>.prototype.keys() : Iterable<number>
TypedArray<T>.prototype.lastIndexOf(searchElement, fromIndex?) : number
get TypedArray<T>.prototype.length : number
TypedArray<T>.prototype.map(callbackfn, thisArg?) : TypedArray<T>
TypedArray<T>.prototype.reduce(callbackfn : (previousValue : any, currentElement : T, currentIndex : number, array : TypedArray<T>) => any, initialValue?) : any
TypedArray<T>.prototype.reduceRight(callbackfn : (previousValue : any, currentElement : T, currentIndex : number, array : TypedArray<T>) => any, initialValue?) : any
TypedArray<T>.prototype.reverse() : This
TypedArray<T>.prototype.slice(start=0, end=this.length) : TypedArray<T>
TypedArray<T>.prototype.some(callbackfn, thisArg?)
callbackfn
がtrue
を返す場合、true
を返します。それ以外の場合はfalse
を返します。some()
は、callbackfn
が初めてtrue
を返す時点で処理を停止します。TypedArray<T>.prototype.sort(comparefn? : (number, number) => number)
comparefn
で指定されたとおりに、この型付き配列をソートします。comparefn
が省略されている場合、小なり演算子(<
)を使用して比較することにより、昇順にソートされます。TypedArray<T>.prototype.toLocaleString(reserved1?, reserved2?)
TypedArray<T>.prototype.toString()
TypedArray<T>.prototype.values() : Iterable<T>
これらのメソッドはすべて配列で使用できるため、動作の詳細については次の2つのソースを参照してください。
copyWithin
、entries
、fill
、find
、findIndex
、keys
、values
。通常の配列メソッドはジェネリックである(配列のような任意のthis
がOK)のに対し、このセクションにリストされているメソッドはジェネリックではありません(this
は型付き配列である必要があります)。
«ElementType»Array
コンストラクタ 各型付き配列コンストラクタの名前は«ElementType»Array
というパターンに従います。ここで«ElementType»
は、冒頭の表にある要素タイプの1つです。つまり、型付き配列には9つのコンストラクタがあります。Int8Array
、Uint8Array
、Uint8ClampedArray
(要素タイプUint8C
)、Int16Array
、Uint16Array
、Int32Array
、Uint32Array
、Float32Array
、Float64Array
です。
各コンストラクタには5つのオーバーロードされたバージョンがあります。引数の数とその型によって動作が異なります。
«ElementType»Array(buffer, byteOffset=0, length?)
buffer
である新しい型付き配列を作成します。指定されたbyteOffset
でバッファへのアクセスを開始し、指定されたlength
を持ちます。length
はバイトではなく、型付き配列の要素数(それぞれ1~4バイト)をカウントすることに注意してください。«ElementType»Array(length)
length
と適切なバッファ(バイト単位のサイズはlength * «ElementType»Array.BYTES_PER_ELEMENT
)を持つ型付き配列を作成します。«ElementType»Array()
length
が0の型付き配列を作成します。空のArrayBufferも作成します。«ElementType»Array(typedArray)
typedArray
と同じ長さおよび要素を持つ新しい型付き配列を作成します。大きすぎる値または小さすぎる値は適切に変換されます。«ElementType»Array(arrayLikeObject)
arrayLikeObject
を配列として扱い、同じ長さおよび要素を持つ新しいTypedArrayを作成します。大きすぎる値または小さすぎる値は適切に変換されます。次のコードは、同じ型付き配列を作成する3つの異なる方法を示しています。
const
tarr1
=
new
Uint8Array
([
1
,
2
,
3
]);
const
tarr2
=
Uint8Array
.
of
(
1
,
2
,
3
);
const
tarr3
=
new
Uint8Array
(
3
);
tarr3
[
0
]
=
0
;
tarr3
[
1
]
=
1
;
tarr3
[
2
]
=
2
;
«ElementType»Array
プロパティ «ElementType»Array.BYTES_PER_ELEMENT
> Uint8Array.BYTES_PER_ELEMENT
1
> Int16Array.BYTES_PER_ELEMENT
2
> Float64Array.BYTES_PER_ELEMENT
8
«ElementType»Array.prototype
プロパティ «ElementType»Array.prototype.BYTES_PER_ELEMENT
«ElementType»Array.BYTES_PER_ELEMENT
と同じです。型付き配列には、通常の配列のようにconcat()
メソッドがありません。回避策として、次のメソッドを使用します。
typedArray
.
set
(
arrayOrTypedArray
,
offset
=
0
)
このメソッドは、既存の型付き配列(または通常の配列)をインデックスoffset
にあるtypedArray
にコピーします。その後、typedArray
が連結するすべての(型付き)配列を保持するのに十分な大きさであることを確認するだけです。
function
concatenate
(
resultConstructor
,
...
arrays
)
{
let
totalLength
=
0
;
for
(
const
arr
of
arrays
)
{
totalLength
+=
arr
.
length
;
}
const
result
=
new
resultConstructor
(
totalLength
);
let
offset
=
0
;
for
(
const
arr
of
arrays
)
{
result
.
set
(
arr
,
offset
);
offset
+=
arr
.
length
;
}
return
result
;
}
console
.
log
(
concatenate
(
Uint8Array
,
Uint8Array
.
of
(
1
,
2
),
Uint8Array
.
of
(
3
,
4
)));
// Uint8Array [1, 2, 3, 4]
DataView
コンストラクタ DataView(buffer, byteOffset=0, byteLength=buffer.byteLength-byteOffset)
buffer
に格納されている新しいDataViewを作成します。デフォルトでは、新しいDataViewはbuffer
全体にアクセスできます。最後の2つのパラメータを使用して変更できます。DataView.prototype
プロパティ get DataView.prototype.buffer
get DataView.prototype.byteLength
get DataView.prototype.byteOffset
DataView.prototype.get«ElementType»(byteOffset, littleEndian=false)
«ElementType»
は次のいずれかです。Float32
、Float64
、Int8
、Int16
、Int32
、Uint8
、Uint16
、Uint32
DataView.prototype.set«ElementType»(byteOffset, value, littleEndian=false)
value
をこのDataViewのバッファに書き込みます。«ElementType»
は次のいずれかです。Float32
、Float64
、Int8
、Int16
、Int32
、Uint8
、Uint16
、Uint32
型付き配列はしばらく前から存在しているため、それをサポートするブラウザAPIがいくつかあります。
ファイルAPIを使用すると、ローカルファイルにアクセスできます。次のコードは、送信されたローカルファイルのバイトをArrayBufferで取得する方法を示しています。
const
fileInput
=
document
.
getElementById
(
'fileInput'
);
const
file
=
fileInput
.
files
[
0
];
const
reader
=
new
FileReader
();
reader
.
readAsArrayBuffer
(
file
);
reader
.
onload
=
function
()
{
const
arrayBuffer
=
reader
.
result
;
···
};
XMLHttpRequest
新しいバージョンのXMLHttpRequest
APIでは、結果をArrayBufferで配信できます。
const
xhr
=
new
XMLHttpRequest
();
xhr
.
open
(
'GET'
,
someUrl
);
xhr
.
responseType
=
'arraybuffer'
;
xhr
.
onload
=
function
()
{
const
arrayBuffer
=
xhr
.
response
;
···
};
xhr
.
send
();
XMLHttpRequest
と同様に、Fetch APIを使用すると、リソースを要求できます。しかし、Promiseに基づいているため、より便利に使用できます。次のコードは、url
が指すコンテンツをArrayBufferとしてダウンロードする方法を示しています。
fetch
(
url
)
.
then
(
request
=>
request
.
arrayBuffer
())
.
then
(
arrayBuffer
=>
···
);
canvas
要素は、スクリプトに解像度依存のビットマップキャンバスを提供します。これを使用して、グラフ、ゲームグラフィックス、アート、その他の視覚イメージを動的にレンダリングできます。
canvas
の2Dコンテキストを使用すると、ビットマップデータをUint8ClampedArray
のインスタンスとして取得できます。
const
canvas
=
document
.
getElementById
(
'my_canvas'
);
const
context
=
canvas
.
getContext
(
'2d'
);
const
imageData
=
context
.
getImageData
(
0
,
0
,
canvas
.
width
,
canvas
.
height
);
const
uint8ClampedArray
=
imageData
.
data
;
WebSocketsを使用すると、ArrayBuffersを介してバイナリデータを送受信できます。
const
socket
=
new
WebSocket
(
'ws://127.0.0.1:8081'
);
socket
.
binaryType
=
'arraybuffer'
;
// Wait until socket is open
socket
.
addEventListener
(
'open'
,
function
(
event
)
{
// Send binary data
const
typedArray
=
new
Uint8Array
(
4
);
socket
.
send
(
typedArray
.
buffer
);
});
// Receive binary data
socket
.
addEventListener
(
'message'
,
function
(
event
)
{
const
arrayBuffer
=
event
.
data
;
···
});
<audio>
と<video>
です。Media Source Extensions APIを使用すると、これらの要素を介して再生されるストリームを作成できます。ArrayBuffers、型付き配列、またはDataViewを介してバイナリデータをストリームに追加できます。postMessage()
を介してWorkerにデータを送信する場合、メッセージ(複製されます)または転送可能なオブジェクトにArrayBuffersを含めることができます。postMessage()
メソッドも使用します。この例は、JPEGファイルをアップロードしてその構造を解析し、画像の高さ、幅などを決定できるWebページです。
JPEGファイルは、セグメント(型付きデータ)のシーケンスです。各セグメントは次の4バイトで始まります。
JPEGファイルはすべてのプラットフォームでビッグエンディアンです。したがって、この例は、DataViewを使用する場合にエンディアンを指定することがいかに重要であるかを示しています。
次の関数processArrayBuffer()
は、実際のコードの短縮版です。混乱を避けるため、いくつかのエラーチェックを削除しました。processArrayBuffer()
は、送信されたJPEGファイルの内容を含むArrayBufferを受け取り、そのセグメントを反復処理します。
// JPEG is big endian
var
IS_LITTLE_ENDIAN
=
false
;
function
processArrayBuffer
(
arrayBuffer
)
{
try
{
var
dv
=
new
DataView
(
arrayBuffer
);
···
var
ptr
=
2
;
while
(
true
)
{
···
var
lastPtr
=
ptr
;
enforceValue
(
0xFF
,
dv
.
getUint8
(
ptr
),
'Not a marker'
);
ptr
++
;
var
marker
=
dv
.
getUint8
(
ptr
);
ptr
++
;
var
len
=
dv
.
getUint16
(
ptr
,
IS_LITTLE_ENDIAN
);
ptr
+=
len
;
logInfo
(
'Marker: '
+
hex
(
marker
)
+
' ('
+
len
+
' byte(s))'
);
···
// Did we find what we were looking for?
if
(
marker
===
0xC0
)
{
// SOF0
logInfo
(
decodeSOF0
(
dv
,
lastPtr
));
break
;
}
}
}
catch
(
e
)
{
logError
(
e
.
message
);
}
}
このコードは、次のヘルパー関数(ここでは示していません)を使用します。
enforceValue()
は、予想される値(最初の引数)が実際の値(2番目の引数)と一致しない場合にエラーをスローします。logInfo()
とlogError()
は、ページにメッセージを表示します。hex()
は、数値を2桁の16進数文字列に変換します。decodeSOF0()
は、セグメントSOF0を解析します。
function
decodeSOF0
(
dv
,
start
)
{
// Example (16x16):
// FF C0 00 11 08 00 10 00 10 03 01 22 00 02 11 01 03 11 01
var
data
=
{};
start
+=
4
;
// skip marker 0xFFC0 and segment length 0x0011
var
data
=
{
bitsPerColorComponent
:
dv
.
getUint8
(
start
),
// usually 0x08
imageHeight
:
dv
.
getUint16
(
start
+
1
,
IS_LITTLE_ENDIAN
),
imageWidth
:
dv
.
getUint16
(
start
+
3
,
IS_LITTLE_ENDIAN
),
numberOfColorComponents
:
dv
.
getUint8
(
start
+
5
),
};
return
JSON
.
stringify
(
data
,
null
,
4
);
}
JPEGファイルの構造に関する詳細情報
型付き配列APIの大部分は、すべての最新のJavaScriptエンジンで実装されていますが、いくつかの機能はECMAScript 6の新機能です。
TypedArray<T>.from()
、TypedArray<T>.of()
TypedArray<T>.prototype.map()
など。TypedArray<T>
がすべての型付き配列クラスのスーパークラスである継承階層。これらがどこでも利用できるようになるまでには時間がかかる可能性があります。いつものように、kangaxの「ES6互換性表」は現状を説明しています。