独自形式の書庫を作り出す超簡単お気軽お手軽足軽尻軽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までしか正確に書き込めません
- 0 byte目
書庫構築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としての常識的な作法です。非常識な方は決して真似しないように(^-^;)