つまり圧縮して自己展開するように作り変えます。今時のbrowserで普通に閲覧可能。programは以下の通り
<!doctype html><html><head>
<meta name=viewport content="width=device-width,initial-scale=1">
<title>sfxhtml</title>
<style>
*{color:snow;font-family:"MS Gothic",monospace;font-size:1em;margin:0}
html,body{overflow:hidden;height:100%}
input,select,label[id]{background:#848;border:1px solid #c6c;border-radius:4px;font-size:1em}
a{color:#acf;text-decoration:none}
a:hover,label:hover{color:#aff}
input,label,select{cursor:pointer;text-shadow:1px 1px #000}
input[type=file]{display:none}
#info{position:absolute;bottom:0;cursor:help;height:1.25em;text-align:left;width:100%;z-index:999}
#uform{width:100%;height:100%;background:#212;background:radial-gradient(circle at center center,#848,#212)}
/* drop area */
ol{border:2px dashed #caf;overflow:auto;background:rgba(48,32,48,.75);margin:0;position:absolute;top:1.4em;bottom:1.25em;left:0;right:0}
ol:empty:before{content:"Drop files here";display:block;text-align:center;position:relative;top:45%}
li{margin-bottom:3px}
.dover{background:#535}
.dpass{background:#323}
</style>
</head><body>
<form id=uform><div style="text-align:center">
output<select id=op><option>raw<option>base64</select>
charset<select id=ip><option>utf8<option value=866>ibm866<option value=l1>latin1<option value=l2>latin2<option value=l3>latin3<option value=l4>latin4<option>cyrillic<option>arabic<option>greek<option>hebrew<option>logical<option value=l6>latin6<option>iso-8859-13<option>iso-8859-14<option value=l9>iso-8859-15<option>iso-8859-16<option value=koi>koi8-r<option>koi8-u<option>mac<option>dos-874<option>cp1250<option>cp1251<option>cp1253<option value=l5>cp1254<option>cp1255<option>cp1256<option>cp1257<option>cp1258<option>x-mac-cyrillic<option>gbk<option>gb18030<option>hz-gb-2312<option>big5<option>euc-jp<option>iso-2022-jp<option>sjis<option>korean<option>iso-2022-kr<option>utf-16<option>utf-16be<option>x-user-defined<option>iso-2022-cn</select>
<label id><input type=file multiple id=load>LOAD</label>
<label id=save>Save</label>
<label id=kill>kill</label>
</div>
<ol id=flist></ol>
</form>
<input type=button value="sfx html creater" id=info>
<script>
let g=a=>Uint8Array.from(a,a=>a.charCodeAt());
const H=[
"<svg onload='",
"fetch``.then(a=>a.arrayBuffer().then(a=>",
"new Response(new Blob([",
"a.slice(",,,
')]).stream().pipeThrough(new DecompressionStream("deflate-raw"))).',
"text",
"().then(a=>write(","a","))",,"'>"],
//for rendering stopper
tag0=g('-->'),
tag1=g('</noscript>'),
tag2=g('</xmp>',g=a=>document.getElementById(a)),
info=g("info"),ol=g("flist"),lf=g("load"),
find=(P,A)=>{
for(let M=P.length,N=A.length-M,i=0,j;i<=N;i+=j||1)
for(j=0;A[i+j]===P[j];)if(++j===M)return 1
},
b64=A=>{
var a=62,b=0,c,z=A.length,B=new Uint8Array((z*4+2)/3),C=new Uint8Array(64);
for(C[a]=43;a;)C[--a]=a>51?a-4:a>25?a+71:a+65;
for(C[63]=47;a<z;B[b++]=C[63&c])c=A[a++]<<16|A[a++]<<8|A[a++],B[b++]=C[c>>18],B[b++]=C[63&c>>12],B[b++]=C[63&c>>6];
return new TextDecoder().decode(B)
},
worker=new Worker(`data:,
self.onmessage=async e=>{
const{action,data}=e.data,z=data.length, s=new CompressionStream('deflate-raw'), w=s.writable.getWriter();
new Response(s.readable).arrayBuffer().then(a=>postMessage({
status:'success',
result:new Uint8Array(a),
inSize:z,
outSize:a.byteLength
}));
//processing
for(let a=0,b=Math.ceil(z<1e4?z/2:z<1e5?z/5:z<1e6?z/10:5e5);a<z;postMessage({status:'progress',p:a,ratio:(a/z*1e4|0)/100}))
await w.write(data.subarray(a,a=Math.min(a+b,z)));
await w.close()
}`);
ol.ondragover=function(e){
this.className="dover";e.preventDefault();
e.dataTransfer.dropEffect='copy'
};
ol.ondragleave=function(){this.className="dpass"};
ol.ondrop=lf.oninput=async function(e){
e.preventDefault(e.stopPropagation());
ol.className="dpass";
var c=0,f=(e.dataTransfer||this).files,r,s=0,t=0,m=op.selectedIndex,cs=ip.value,st=new Date;
for(e of f)t+=e.size;
//write packed data
let pf=r=>e.arrayBuffer().then(a=>{
worker.postMessage({data:new Uint8Array(a)});
worker.onmessage=f=>{
let d=f.data;
if(d.status==="progress")return info.value=info.style.width=((s+d.p)/t*1e4|0)/100+"%";
let a=new Uint8Array(d.result),b=d.inSize,o=m?0:d.outSize,n=e.name.replace(/\.\w+$/,""),li=document.createElement("li"),O=H.slice();
if(cs!="utf8")O[7]="arrayBuffer",O[9]='new TextDecoder("'+cs+'").decode(a)';
if(m)O[1]="",O[3]="Uint8Array.from(atob`",O[4]=b64(a),O[5]="`,a=>a.charCodeAt()";
else O[11]="))",O[13]=f=find(tag0,a)?find(tag1,a)?find(tag2,a)?"<plaintext hidden>":"<xmp hidden>":"<noscript>":"<!\--",O[4]=O.join``.length+f.length-1;
s+=b,O=O.join``,c+=o+=O.length,li.innerHTML=`<a href=${URL.createObjectURL(new Blob(m?[O]:[O,a]))} download='${n}.gz.htm'>${n}.gz.htm</a> <input type=button value="${b+" -> "+o}">`;
ol.appendChild(li);
r(info.value=info.style.width=(s/t*1e4|0)/100+"%")
}
});
for(e of f)await new Promise(pf);
info.value=`${t+" -> "+c}(${(c/t*1e4|0)/100}%), ${new Date-st}ms`
};
// remove files
g("kill").onclick=function(){
for(var e=ol.getElementsByTagName("a"),b=0,c;c=e[b++];)c.download&&URL.revokeObjectURL(c.href);
if(b>1)info.value="removed all files",ol.innerHTML=""
};
// save files
g("save").onclick=function(){
for(var b=ol.getElementsByTagName("a"),c=0,d;d=b[c++];)d.click();
if(c<2)b=this,b.innerText="miss",setTimeout(function(){b.innerText="Save"},500)
}</script>
仕様
圧縮ではCompressionStream、展開ではDecompressionStream様を利用。入力されたhtmlは単一のsvg要素と圧縮文からなるhtmlに変換され、document.writeで展開文を書き出します。元と同じように描画されるとは限りません。
例えば、ふざけて <body onload="..."> を複数記述しているとか、非同期処理を多数搭載しているとか、そんなpageは動作が怪しいです。
説明書
- output
- raw
圧縮文が無変換binary dataとなります
- base64
圧縮文がbase64変換されます。圧縮率劣化しますが安全
- raw
- charset
文字符号をここで指定した値として入力fileを読み込みます
- LOAD
file読み込み。複数選択可能
- Save
圧縮結果を全て保存
- kill
圧縮結果を全て破棄
- Drop files here
ここに複数fileをまとめて投下できます