5
3

Blobのコンストラクタ引数にTypedArrayとか直接使わないでください

Last updated at Posted at 2024-09-18

TD; LR;

JavaScriptのBlobのコンストラクラの第一引数について、仕様を理解せずつかうと上手くいかないこともある

Blob第一引数

MDNの記述では

array
反復可能オブジェクト、例えば Array などです。その中身が ArrayBuffer、
TypedArray、DataView、Blob、文字列などのオブジェクト、
またはそのようなオブジェクトの何れかが混合したもので、それが Blob の中に入れられます。
ここで文字列は UTF-8 で符号化されたものであり、 JavaScript におけるふつうの 
UTF-16 の文字列ではありません。

本来は

const binary = new Uint8Array([11,22,33,44])
const blob = new Blob([binary]) // binaryが配列の中にある

と書いてほしいという記述なんですが、そもそもTypedArray(Uint8Arrayとかのこと)も反復可能オブジェクトですよね?そのままいれてはいけないんですか?って話です

const createLink = (blob:any, title:string, download:string) => {
    // @ts-ignore
    const url = URL.createObjectURL(blob)
    const anchor = document.createElement('a')
    anchor.innerHTML = title
    anchor.href = url
    anchor.download = download
    anchor.style.display = 'block'
    return anchor
}
const main = async() => {
    const numbers = [11,22,33,44,-100]
    const binary = new Int8Array(numbers)

    // Arrayの要素としてUint8Arrayをいれている
    const blob = new Blob([binary])

    // Uint8Arrayをそのまま第一引数にしている
    // @ts-ignore
    const blob2 = new Blob(binary)
    // raw arrayを第一引数にしている
    // @ts-ignore
    const blobNumbers = new Blob(numbers)
    
    const anchor0 = createLink(blob, 'blob correct', 'typedarray.raw')
    const anchor1 = createLink(blob2, 'blob incorrect', 'typedarray-wrong.raw')
    // @ts-ignore
    const anchor2 = createLink(blobNumbers, 'array', 'blobarray.raw')

    document.body.appendChild(anchor0)
    document.body.appendChild(anchor1)
    document.body.appendChild(anchor2)
}
document.addEventListener("DOMContentLoaded", (event:unknown) => {
    main()
})

ダウンロードリンクを3つだしているわけですが、

  • Arrayの要素にTypedArrayをいれたパターン
  • Arrayの要素にせず直接Int8Arrayをつめたパターン
  • 数値のArrayをつめたパターン
    です
    おのおのダウンロードすると以下のような「バイナリファイル」が保存されます

1つめ

0B16212C 9C

2つめ

31313232 33333434 2D313030

3つめ

31313232 33333434 2D313030

2つめと3つめはテキストエディタで開くと以下のとおりになります

11223344-100

なにがおきているのか?

Blobのコンストラクタの定義はECMA-262で定義されるWebIDLによれば以下の通り記述されるものです

[Constructor(optional sequence<BlobPart> blobParts,
             optional BlobPropertyBag options),
 Exposed=(Window,Worker), Serializable]
interface Blob {
...
typedef (BufferSource or Blob or USVString) BlobPart;
...

https://github.com/microsoft/TypeScript-DOM-lib-generator/blob/e2a2072d804999180f45c43f102d2ee604fade65/inputfiles/idl/File.widl
sequenceは型を取れるリストですが、こちらは長いので
https://triple-underscore.github.io/infra-ja.html#list
とかを読むといいです
そしてsequenceに取る型はBlobPartです
BlobPartはBlob,USVString,BufferSourceのいずれかです
ここで

type BufferSource = ArrayBufferView | ArrayBuffer;

ArrayBufferViewは以下を参照してください
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer/isView

結局なにかというと文字列(USVStringは16bit表現の文字列)かArrayBuffer(View)か、Blobの配列がBlobのコンストラクタの第一引数でとることのできる型になり、ArrayBufferはそのままバイナリを、そうでないものは文字列表現に変換されBlobにおさめられるることとなります

ですので[TypedArray]で生成されたBlobではバイナリが、TypedArrayを直接指定するとSerializeされた文字列が保存されます
つまり[11,22,33,44,-100]はバリナリではなく文字列として11223344-100としてBlobに保存されることになります

Blobの第一引数がsequenceなのはなぜだろう?

複合的なデータを構築するためでしょう。テキストであれ、バイナリであれ、順列に構成していくことで多様なデータ形式に対応するためにこのような仕様になってると思います(根拠はない)

typescriptでは?

WebIDLから生成された制約が有効になるためサンプルのように無効化しない限りエラーになります
これを無視しない限り、変な問題には遭遇しないことが期待されます
第一引数はBlobPart[]になります(詳細は上述の通りです)

まとめ

Blobの第一引数についての謎をおいかけました
この内容を理解した上で、最初のMDNの記述を読むと「正しく」理解できると思います

雑感

調べると言語/API仕様のお気持みたいなものがわかる気がする

5
3
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
5
3