2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JavaScript: HTMLに生binary埋め込み(2)

Last updated at Posted at 2024-10-28

過去の記事の続編、今回は余計な手間をかけずにHTMLに生のbinary dataを埋め込む方法を紹介します。
<noscript>に埋め込む所までは同じですが、取得法を工夫します。方法はいたって単純で、fetchで自身を読み込みarrayBufferの指定範囲を取り出すのです。

非同期処理版
<script>
fetch("#").then(a=>a.arrayBuffer().then(a=>console.log(a.slice(98))))
</script><noscript>!"#${%&'()`}
同期処理版
<script type=module>
let a=await(await fetch("#")).arrayBuffer();
console.log(a.slice(111))
</script><noscript>!"#${%&'()`}

できれば<noscript>はhtmlの最後の要素にした方が良い。そうすれば閉じtagを省略しても問題にならない(多分)。ちなみにfetch("#")の代わりにfetch("?")fetch(location.href)fetch("")でも良い(多分)。XMLHttpRequestで代用してもいい

<script>
with(new XMLHttpRequest)open("GET","#"),send(responseType="arraybuffer"),onload=a=>console.log(new Uint8Array(response.slice(160)))
</script><noscript>!"#$%&'()`}

</script>以降の代替案として

強固な表現
</script><noscript><!-- binary data
脆弱だけど短い表現
</script><!-- binary data

などというのもありかな。--></noscript>は勿論省略

利点

  • Unicode番号0,13,128~159などといったろくでもない文字を変換する必要がない
  • <meta charset=l1>を記述する必要もない(charsetは何でも良い)
  • JavaScriptの記述が単純

欠点

  • local環境で動作しないbrowserもある

応用編

それでは自己展開書庫を生成するprogramを改良してみます

<meta charset=utf8><title>arc-sfx</title>
<h2>自己展開書庫生成(self-extracting archive)</h2>
<p>書庫はhtml形式なのでbrowserで閲覧可能。構造は非常に単純で、file名、file尺、file内容が圧縮されて格納される
<p>複数file選択可能<input multiple type=file id=i>
<p>html<select id=type><option>raw<option>shrink<option>svg</select>
<script>
const H=[//html(decoders)
	'<script>fetch("").then(a=>a.arrayBuffer().then(a=>new Response(new Blob([a.slice(556)]).stream().pipeThrough(new DecompressionStream("deflate-raw"))).arrayBuffer().then(a=>{for(a=new Uint8Array(a),b=0;a[b]+1;l.innerHTML+=`<li><a download="${f}"href=${URL.createObjectURL(new Blob([a.subarray(b,b+=d)]))}>${f}</a> ${d}`){var b,c=32,d=a[b++],f=d>>5;for(d&=31;f--;c*=256)d+=c*a[b++];for(f=b;a[b++];);f=new TextDecoder().decode(new Uint8Array(a.subarray(f,b-1)))}})))<\/script><button onclick="for(let a of links)a.click()">save</button><ol id=l></ol><noscript>',
	atob`PHNjcmlwdD5mb3IoZT0ncmUSZj0RKS4QJHtmfQ8pKSkOcnJheQx1YmEMKAtlY29kZQl0EmFtKAhmb3IoBxB0aGVuKGE9PgZhW2IrK10FbmV3IAQoBEJsb2IoW2EucwMEVWludDhBDChhAi5hDEJ1ZmZlcigGAWZldGNoKCIiBmEBBFJlc3BvbnNlA2xpY2UoNTMwKV0QcwgQcGlwZVRocm91Z2goBERlY29tcBJzc2lvblMIImRlZmxhdGUtcmF3Ig4BewdhPQIpLGI9MDthW2JdKzE7bC5pbm5lckhUTUwrPWA8bGk+PGEgZG93bmxvYWQ9Ig8iaBIRJHtVUkwuYxJhdGVPYmplY3RVUkwDC2IsYis9ZCldKSl9Pg88L2E+ICR7ZH1gKXt2YXIgYixjPTMyLGQ9BSwRZD4+NTsHZCY9MzE7Zi0tO2MqPTI1NilkKz1jKgU7BxFiOwU7KTsRBFRleHRECXIoEGQJKAIucwtmLGItMQ59fQ4nO2E9L1sBLRJdLy5leGVjKGUpOyl3aXRoKGUuc3BsaXQoYSkpZT1qb2luKHNoaWZ0KCkpO2V2YWwoZSk8L3NjcmlwdD48YnV0dG9uIG9uY2xpY2s9ImZvcihsZXQgYSBvZiBsaW5rcylhLmNsaWNrKCkiPnNhdmU8L2J1dHRvbj48b2wgaWQ9bD48L29sPjxub3NjcmlwdD4`,
	atob`PHN2ZyBvbmxvYWQ9J2ZvcihlPSYjMzlmPRZyZRUpLhRsaRNvbhIke2Z9ESkpKRBlY29kZQ9ycmF5DnViYQ4oDGMTY2sLdBVhbSgJYnV0dBIIZm9yKAcUdGhlbihhPT4GYVtiKytdBW5ldyAEKARCbG9iKFthLnMDBFVpbnQ4QQ4oYQIuYQ5CdWZmZXIoBgF3cml0ZWA8CCASCz0iB2xldCBhIG9mIBNua3MpYS4LKCkiPnNhdmU8Lwg+PG9sIGlkPWw+YDtmZXRjaCgiIgZhAQRSZXNwEnNlAxNjZSg1MzkpXRRzCRRwaXBlVGhyb3VnaCgERGVjb21wFXNzaRJTCSJkZWZsYXRlLXJhdyIQAXsHYT0CKSxiPTA7YVtiXSsxO2wuaW5uZXJIVE1MKz1gPBM+PGEgZG93bmxvYWQ9IhEiaBUWJHtzZWxmLlVSTC5jFWF0ZU9iamVjdFVSTAMMYixiKz1kKV0pKX0+ETwvYT4gJHtkfWApe3ZhciBiLGM9MzIsZD0FLBZkPj41OwdkJj0zMTtmLS07Yyo9MjU2KWQrPWMqBTsHFmI7BTspOxYEVGV4dEQPcigUZA8oAi5zDGYsYi0xEH19ECYjMzkgO189L1sBLRZdLy5leGVjKGUpOyl3aXRoKGUuc3BsaXQoXykpZT1qb2luKHNoaWZ0KCkpO2V2YWwoZSknPjwvc3ZnPjxub3NjcmlwdD4`
];
i.oninput=async e=>{
	var a=0,b,c,d,f=i.files,r,s=0,n=[],o,p=0,h=H[type.selectedIndex];
	// file名収集
	for(e of f)n[p++]=r=new TextEncoder().encode(e.name),a+=r.length+e.size+6;
	o=new Uint8Array(a);a=p=0;
	// fileの中身と大きさ収集
	for(e of f){
		e=new Uint8Array(await e.arrayBuffer(b=a));
		for(c=32,s+=d=r=e.length;d-=o[a++]=d&c-1;c=256)d/=c,o[b]+=32;
		o.set(c=n[p++],a,a-=~c.length);
		o.set(e,a,a+=r)
	}
	// 圧縮
	o=await new Response(new Blob([o.subarray(0,a)]).stream().pipeThrough(new CompressionStream("deflate-raw"))).arrayBuffer();
	// 結果
	l.innerText=p+" files("+s+" bytes)\u2192"+(h.length+o.byteLength)+" bytes";
	l.href=URL.createObjectURL(new Blob([h,o]));i.value=""
}
</script><a id=l download=a.htm>

設定

html

  • raw

    復号器原型のまま

  • shrink

    復号器圧縮(最小)

  • svg

    復号器圧縮 + script要素排除。復号後に全html更新

ちなみにH[1]H[2]が奇妙な記述です。わざわざatobからhtmlを作っています。本来はその必要ないですが、qiitaの記事は一部の文字が正常に表示できないので、やむを得ずbase64変換しちゃいました。それではcodepen召喚

See the Pen sfx archive creater2 by xezz (@xezz) on CodePen.

js fileにも

jsの末尾に複数行comment(/* .... */)を追加して、その中身に任意のbinary dataを放り込む手もあります。ただしcomment終了文字列が混入してはいません(*/)。混入している場合には特別な文字でescapeする必要があります(escape文字自体もescapeする)

a.js
fetch("a.js").then(a=>a.arrayBuffer().then(a=>self.a=a.slice(84,a.byteLength-2)))
/*binary data*/
<script src="a.js"></script><script>
setTimeout(a=>cosole.log(self.a),500)
<script>

setTimeoutを使う理由はfetchが非同期だからです。これが厄介…

2
2
0

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
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?