1
0

JavaScriptで独自の書庫を作る

Last updated at Posted at 2024-02-25

独自形式の書庫を作り出す超簡単お気軽お手軽足軽尻軽programを紹介します。今やJavaScriptでも容易に実装できる時代となりました。外部jsにも頼る必要はありません。

仕様

  • 書庫には1個のfileにつきfile尺、file名、file中身、という順番に配置
  • file名はUTF8形式で書き込む。終端に0を書き込む
  • file尺の書き込み量は可変長。file尺をLとすると...
    • 0 byte目

      上位3bitsにbyte数、下位5bitsにLの下位5bits

    • 1 byte目

      L>>5&255

    • 2 byte目以降

      L>>5+8*n&255

    という具合なので書き込み量は、Lが31以下なら1 byte、Lが8191以下なら2 bytes、2097151以下なら3 bytes、53687091以下なら4 bytes... などという内容に違いない。ちなみに64 bits浮動小数点数で計算している都合上、Lは53 bitsまでしか正確に書き込めません

書庫構築program

html+jsで以下のようになります。

<input multiple type=file id=i><script>
i.onchange=async function(e,n=[]){
	for(e of this.files){
		let a=0,b=new Uint8Array(await e.arrayBuffer()),o=new Uint8Array(7),c=32,d=b.length;
		for(;d-=o[a++]=d&c-1;c=256)d/=c,o[0]+=32;
		n.push(o.subarray(0,a),e.name+"\0",b)
	}l.innerText=l.href=URL.createObjectURL(new Blob(n))
}
</script><a id=l download>
  • input要素にmultiple属性を付ける事で複数File選択可能に
  • file尺(b.length)とfile名(e.name)とfile本体(e.arrayBuffer())を出力配列にぶち込む
  • 出力配列からURLに変換してlink生成

最低限にも程があります…

使い方

  • File buttonを押下してFile選択窓が現れたら、Fileを範囲選択して開いて下さい。もしくは複数FileをFile buttonに直接Drag & Dropして下さい
  • 処理が完了するとそれらをまとめたFile(書庫)のlinkが生成されます。clickすればdownload可能

書庫展開program

<input type=file id=i>
<input type=button value=save onclick="for(let a of links)a.click()">
<input type=button value=kill onclick="for(let a of links)self.URL.revokeObjectURL(a.href);l.textContent=i.value=''">
<script>
i.onchange=async function(){
	for(var A=new Uint8Array(await this.files[0].arrayBuffer()),a=0,z=A.length;a<z;l.innerHTML+='<li><a download="'+f+'" href='+URL.createObjectURL(new Blob([A.subarray(a,a+=d)]))+'>'+f+'</a> '+d){
		var c=32,d=A[a++],f=d>>5;
		for(d&=31;f--;c*=256)d+=c*A[a++]; //read size
		for(f=a;A[a++];);
		f=new TextDecoder().decode(new Uint8Array(A.subarray(f,a-1)))
	}
}
</script><ol id=l>
  • fileを読み込みUint8Arrayに変換
  • file尺を解読
  • file名の終端まで読み飛ばしてその範囲を確定
  • 配列からURLに変換してlink生成

使い方

  • File buttonを押下してFileを1個開いて下さい。もしくはFileを直接Drag & Dropして下さい
  • 処理が完了すると書庫に含まれているFileとその尺が列挙されます。そのlinkをclickすればFileをdownload可能
  • killを押下するとFileへのlinkを全て削除
  • saveをclickすると成果物を全てdownload可能

圧縮書庫構築program

手始めに書庫に必要な領域を計算。無圧縮版とは異なりfile尺、file名、file中身の3点をUint8Arrayに詰め込んでいきます。そしてCompressionStream様にそれを圧縮してもらいます。完全にtar.gzの劣化書庫ですね

<input multiple type=file id=i><script>
i.onchange=async function(e){
	var a=0,c,d,f=this.files,i,r,n=[],o;
	for(e of f)n.push(r=new TextEncoder().encode(e.name)),a+=r.length+e.size+6;
	o=new Uint8Array(a);a=0;
	for(e of f){
		e=new Uint8Array(await e.arrayBuffer(i=a));
		for(c=32,d=r=e.length;d-=o[a++]=d&c-1;c=256)d/=c,o[i]+=32;
		o.set(c=n.shift(),a,a-=~c.length);
		o.set(e,a,a+=r)
	}
	l.href=URL.createObjectURL(new Blob([f=await new Response(new Blob([o.subarray(0,a)]).stream().pipeThrough(new CompressionStream("deflate-raw"))).arrayBuffer()]))
	l.innerText=f.byteLength
}
</script><a id=l download>

圧縮書庫展開program

DecompressionStream様が暗躍している以外は無圧縮版とほぼ同じ

<input type=file id=i>
<input type=button value=save onclick="for(let a of links)a.click()">
<input type=button value=kill onclick="for(let a of links)self.URL.revokeObjectURL(a.href);l.textContent=i.value=''">
<script>
i.onchange=async function(){
	for(var A=new Uint8Array(await new Response(new Blob([await this.files[0].arrayBuffer()]).stream().pipeThrough(new DecompressionStream("deflate-raw"))).arrayBuffer()),a=0,z=A.length;a<z;l.innerHTML+='<li><a download="'+f+'" href='+URL.createObjectURL(new Blob([A.subarray(a,a+=d)]))+'>'+f+'</a> '+d){
		var c=32,d=A[a++],f=d>>5;
		for(d&=31;f--;c*=256)d+=c*A[a++]; //read size
		for(f=a;A[a++];);
		f=new TextDecoder().decode(new Uint8Array(A.subarray(f,a-1)))
	}
}
</script><ol id=l>

まとめて実演

See the Pen create original archive by xezz (@xezz) on CodePen.

備考

何気に</a></ol>等の閉じtagを省略していますが、html最後の要素という事で動作上は無問題。むしろcode golferとしての常識的な作法です。非常識な方は決して真似しないように(^-^;)

1
0
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
1
0