ローカルストレージの容量制限
ローカルストレージ(localStrage)の容量は、ブラウザごとに5MBらしいです。 ユーザーがブラウザでアクセスした全てのページで、何かのスクリプトやプログラムが少しずつ消費すれば、枯渇は早そうな気がします。
保存データは全て「1バイト」文字ですが、クッキー(Cookie)に至っては4kBが上限で、「サイトの動作がオカシイ時はクッキーを削除」と良く言いますが、4kBではそれもありがちかも。
現在のデジカメは、1コマで5MBを超えるのが普通にありますが、こんな小容量でやりくりしているのは、ページ表示をコントロールする情報を保存してブラウザが常にアクセスする記憶域だからでしょう。
やはり、ローカルストレージの5MBは贅沢に使えない気がします。 これまで作って来た JavaScriptツールでは、「最小限に節約する」方針でした。「Authentic Reader」は、ユーザーアイコンのSRCデータを省いて、データは「フォロワーのアメーバID」「フォロー登録日時」「属性フラグ」のみにしています。 通常は 30件前後のデータで、当ブログでは「1200文字」でした。 つまり1.2kBです。
この程度の使い方なら上等という気もしますが、でもユーザーが削除しないと消えないのがローカルストレージの仕組みです。 JavaScriptツールを作る際には、不要なストレージデータは削除できる様にするのが、私のポリシーです。
今回は、「Authentic Reader」のローカルストレージの処理をするツールに関するページです。
ストレージの情報は判り難い
今回、ローカルストレージの容量の上限をあらためて調べたのですが、少し混乱しました。 というのは、Mac関連ですが、スマホ写真等をクラウド保存する機能が「ローカルストレージの上限」のエラーを表示するらしく、そちらに関する情報が沢山ヒットしたからです。
これは、スマホ等の「ローカル」なメモリ「ストレージ」を、ローカルストレージと言っただけで、ブラウザが管理する「ローカルストレージ」とは全く別物でしょう。
それはいいとして、色々調べていると「サイト毎に5MB」と読める様な情報がありました。 しかし、もしそうなら無制限に使える事になるので、やはり「ブラウザごとに全て合わせて5MB」という、これまでの認識に落ち着きました。 ローカルストレージに関しては、以下のページの説明が良く判ります。
「Authentic Reader Storage Manager」
「Authentic Reader」は、ver.0.3 からローカルストレージの保存Keyを変更しました。 もしver.0.2までのテスト版を使っていると、旧keyは放棄されて新Keyのデータが使われます。 不要になった 旧keyのデータは「DevTools」を使えば消せますが、データにアクセスし難く、一般の方には勧めにくい機能です。
そこで「Storage Manager」を制作しました。 これは「Authentic Reader」の専用ツールで、「Authentic Reader」が扱うストレージデータに簡単にアクセス出来て、確実な処理ができます。
実は、このツールを作ったのは、今後もこの種の追加ツールが必要になりそうな気がしたからです。 過去のツールの制作時でも、ストレージ保存様式を変えた時に、旧データを新バージョンに引継ぐ工夫をして来ました。 しかし、今回の様に保存Keyが変わると、引継ぎが少し難しくなって来ます。 そういう機構を無理にツールに追加すると、半分がバージョン処理のツールになりかねない。
「Authentic Reader」の場合は、専用の別ツールを用意した方が早いと考えました。 今回のノウハウは、将来に同じ事態に出会った時にも使えますし。
操作の前に
「Authentic Reader Storage Manager」は、ストレージデータの整理のために使うツールで、当然「非常駐型」ツールです。 起動すると他のブログ管理操作が出来なくなります。
▪このツールを使用する前に「Authentic Reader」を前もって OFFにしてください。
▪「Tampermonkey」のダッシュボードで「Authentic Reader Storage Manager」を ONにします。
▪「フォロワー管理」のページだけでなく、「ブログ管理画面」の殆どのページでこのツールは起動します。 どの画面で操作をしても結果は同じです。
▪「Authentic Reader」が生成する「ストレージデータの削除」「key間のコピー」が出来ます。 これを使うと ver.0.2以前のデータを、ver.0.3 以降で利用できます。
▪ ストレージデータの処理が終わったら、「Tampermonkey」のダッシュボードで、このツールを OFFにしてください。
起動時の初期画面
「Storage Manager」を ONにして「ブログ管理」の任意のページを開くと、下の様に背景が暗転して、小型の「表」が表示されます。
これは「Authentic Reader」がローカルストレージに保存した全データの一覧です。
最上段の「Readers_AR」は、ver.0.1 ~ ver.0.2 が保存したデータです。 それ以外は ver.0.3 が保存したデータです。 ver.0.3 以降は ID別でデータを保存していて、上図の「AR_ameblouser」は私の「テスト用ブログ」、「AR_personwritep」は当ブログのデータです。
「表」の左列は保存「key」、右列は「値」です。「Authentic Reader」の値は「フォロワー履歴」のデータで、横スクロールで全内容が表示できます。「表」は編集できませんが、テキストデータなのでコピー出来ます。
ストレージデータの処理操作
今回の「Storage Manager」の主目的は、簡単に旧いストレージデータを削除する事です。 上図の「Readers_AR」は旧データで、ver.0.3以降は使わないので不要なデータです。 また、「Authentic Reader」自体を試したが使わないという場合も、このツールで不要データをローカルストレージから削除できます。 僅かの容量ですが、要らない荷物は捨てるのが正解です。
以下は「Readers_AR」のストレージデータを処理する具体例です。
データの削除
❶ 処理対象の「Readers_AR」の行を「左Click」します。
選択した行の「Key」列に淡いブルーの背景が表示され、下側にこのデータに関して可能な処理が表示されます。(青枠は説明のために描き入れたものです)
▪旧バージョン(1行目)のデータ自身を「削除」できます。
▪旧バージョンのデータを、新バージョンに「コピー」できます。 新バージョンのデータを旧バージョンにコピーする意味はないので、そのメニューはありません。
▪「キャンセル」は初期画面に戻ります。
❷「削除」を「左Click」すると確認画面が出ます。
▪「Readers_AR」の行全体に淡い青背景が表示されます。 変更される部分を背景色で示す様にしています。
❸ 上の確認画面で「削除」を「左Click」すると、データ削除が行われます。
▪削除した行のデータは完全に失われ、戻すことが出来ません。
▪削除後に画面がリロードされ、「表」が更新された初期画面になります。
データのコピー
データのコピーは「値」を他の「Key」にコピーする操作です。 コピーされる側は「Key」がそのままで「値」が変わります。「Authentic Reader」は「key」を使ってデータにアクセスするので、「Keyが示すID」のブログの情報が変わります。
❹ 初期画面で、コピー元にする「Readers_AR」の行を「左Click」した所です。
操作例として、三行目のコピー操作を選択します。 操作は「コピー元」「コピー先」を取り違えない様にします。
➎「Raeders_AR」を「AR_personwritep」に「コピー」を「左Click」すると、下のコピーの確認画面が出ます。
▪「コピー先」の「値」が書換えられますが、元のkeyに「_n」の自動連番を付けたバックアップを別に作ってから、コピーが行われます。
❻ 上の確認画面で「コピー」を「左Click」すると、コピーが実行されてページがリロードされ、「表」が更新されます。
▪バックアップが出来たので、データが1件増えています。
▪コピー操作は、ver.0.2以前のデータを ver.0.3以降に引き継ぐための機能ですが、実際には殆ど必要が無いでしょう。 しかし、必ずバックアップが生成されるので、データ整理では融通が利きます。 例えば、誤ったコピー操作をした場合は、再コピーで元に戻せます。 このコードを他で利用する際に必要となり得る機能です。
▪重要なのは、実際に使われる「key」とその「値」で、それが目的の状態になれば、後は「削除」で不要なデータを削除します。
「Authentic Reader Storage Manager」
「Authentic Reader Storage Manager」は「Authentic Reader」が生成するストレージデータを整理するための補助ツールです。
このツールは Chrome / Edge / Firefox の拡張機能「Tampermonkey」上で動作します。 以下に、このツールの導入手順を説明します。
「Tampermonkey」にスクリプトを登録します
◎「Tampermonkey」の「+」マークの「新規スクリプト」タブを開きます。
◎「新規スクリプト」には、最初からテンプレートが記入されています。 これは全て削除して、完全に空白の編集枠に 下のコードをコピー&ペーストします。
〔コピー方法〕 軽量シンプルなツール「PreBox Button 」を使うと
コード枠内を「Ctrl+左Click」➔「Copy code 」を「左Click」
の操作で、掲載コードのコピーが可能になります。
◎ 最後に「ファイル」メニューの「保存」を押すと、ツールが使用可能になります。
〔 Authentic Reader Storage Manager 〕 ver. 0.1
// ==UserScript== // @name Authentic Reader Storage Manager // @namespace http://blog.ameba.jp // @version 0.1 // @description 自動プログラムの時限フォローを判定する // @author Ameba Blog User // @match https://blog.ameba.jp/ucs/* // @exclude https://blog.ameba.jp/ucs/entry* // @icon https://www.google.com/s2/favicons?sz=64&domain=ameba.jp // @grant none // ==/UserScript== let readers_value; // フォロワー登録 配列要素 let readers={}; // フォロワー登録データ function write_local(key){ localStorage.setItem(key, readers_value); } // ローカルストレージ 保存名 function read_local(key){ return localStorage.getItem(key); } // ローカルストレージ 保存名 let t_style= '.wrapper { position: fixed; top: 0; left: 0; z-index: 10; '+ 'width: 100vw; height: 100vh; background: #00000075; } '+ '.wrap_ar { position: fixed; top: 140px; left: calc(50% - 408px); '+ 'padding: 20px; background: #fff; } '+ '#storage_man { border-collapse: collapse; } '+ '#storage_man th, #storage_man td { font: normal 16px Meiryo; '+ 'padding: 3px 8px 1px; border: 1px solid #666; } '+ '#storage_man .th1 { width: 240px; } '+ '#storage_man .th2 { width: 500px; } '+ '#storage_man .td2 div { overflow-y: hidden; overflow-x: scroll; '+ 'white-space: nowrap; width: 500px; }'; let table= '<div class="wrapper">'+ '<div class="wrap_ar">'+ '<table id="storage_man">'+ '<tbody>'+ '<tr>'+ '<th class="th1">処理する関連Key</th>'+ '<th class="th2">値</th></tr>'+ '</tbody></table><style>'+ t_style +'</style></div></div>'; if(!document.querySelector('#storage_man')){ document.body.insertAdjacentHTML('beforeend', table); } if(read_local('Readers_AR')){ //「Readers_AR」のkey名のストレージ登録を表示 readers_value=read_local('Readers_AR'); tr_add('Readers_AR', readers_value); } for(let i=0; i<localStorage.length; ++i){ //「AR_UserID」のkey名のストレージ登録を表示 if(localStorage.key(i).startsWith('AR_')){ let key_with_id=localStorage.key(i); readers_value=read_local(key_with_id); tr_add(key_with_id, readers_value); }} function tr_add(key, value){ let add= '<tr>'+ '<td class="td1">'+ key +'</td>'+ '<td class="td2"><div>'+ value +'</div></td></tr>'; let table_tbody=document.querySelector('#storage_man tbody'); if(table_tbody){ table_tbody.insertAdjacentHTML('beforeend', add); }} edit(); function edit(){ let t_tr=document.querySelectorAll('#storage_man tr'); for(let k=1; k<t_tr.length; k++){ // 1から開始 t_tr[k].onclick=function(){ action_clear(); let select=t_tr[k].querySelector('.td1'); select.style.background='aliceblue'; let key=select.textContent; edit_key(key); } }} function edit_key(key_){ let style_ac= '.action_ar { margin-top: 30px; padding: 10px 20px; border: 1px solid #ccc; } '+ '.action_ar p { font: normal 16px/40px Meiryo; } '+ '.action_ar input, .action_ar button { appearance: auto; font: normal 16px Meiryo; '+ 'height: 30px; padding: 2px 6px 0; margin: 0 10px; }'; let action; if(key_=='Readers_AR'){ action= '<div class="action_ar">'+ '<p>■ <b>'+ key_ +'</b> のデータを<input type="submit" value="削除" '+ 'act="1, ,'+ key_ +'">する</p>'; for(let i=0; i<localStorage.length; ++i){ //「AR_UserID」のkey名のストレージ登録を表示 if(localStorage.key(i).startsWith('AR_')){ let key_with_id=localStorage.key(i); action+= '<p>■ <b>'+ key_ +'</b> のデータを <b>'+ key_with_id +'</b> に'+ '<input type="submit" value="コピー" '+ 'act="2,'+ key_ +','+ key_with_id +'">する</p>'; }} action+= '<p style="text-align: right">'+ '<button type="submit" class="return_back">キャンセル</button></p>'+ '<style>'+ style_ac +'</style></div>'; } else{ action= '<div class="action_ar">'+ '<p>■ <b>'+ key_ +'</b> のデータを<input type="submit" value="削除" '+ 'act="1, ,'+ key_ +'">する</p>'; for(let i=0; i<localStorage.length; ++i){ //「AR_UserID」のkey名のストレージ登録を表示 if(localStorage.key(i).startsWith('AR_')){ let key_with_id=localStorage.key(i); if(key_!=key_with_id){ action+= '<p>■ <b>'+ key_ +'</b> のデータを <b>'+ key_with_id +'</b> に'+ '<input type="submit" value="コピー" '+ 'act="2,'+ key_ +','+ key_with_id +'">する</p>'; }}} action+= '<p style="text-align: right">'+ '<button type="submit" class="return_back">キャンセル</button></p>'+ '<style>'+ style_ac +'</style></div>'; } let wrap_ar=document.querySelector('.wrap_ar'); if(wrap_ar){ if(document.querySelector('.action_ar')){ document.querySelector('.action_ar').remove(); } wrap_ar.insertAdjacentHTML('beforeend', action); } let return_back=document.querySelector('.return_back'); if(return_back){ return_back.onclick=function(){ document.querySelector('.action_ar').remove(); action_clear(); }} setTimeout(()=>{ let action_input=document.querySelectorAll('.action_ar input'); for(let k=0; k<action_input.length; k++){ action_input[k].onclick=function(){ let arg=action_input[k].getAttribute('act'); action_do(arg); }}}, 200); function action_do(arg){ let n=arg.split(',')[0]; let key1=arg.split(',')[1]; let key2=arg.split(',')[2]; let action_ar=document.querySelector('.action_ar'); if(n=='1'){ action_ar.innerHTML= '<p>登録key: <b>'+ key2 +'</b> のデータを削除します</p>'+ '<p>この操作で削除されたデータは戻せません '+ '<input class="do_action" value="削除" type="submit">'+ '<input class="no_action" value="キャンセル" type="submit"></p>'+ '<style>'+ style_ac +'</style>'; action_color(key2); let do_=action_ar.querySelector('.do_action'); do_.onclick=function(){ localStorage.removeItem(key2); window.location.reload(true); } let no_=action_ar.querySelector('.no_action'); no_.onclick=function(){ action_clear(); action_ar.remove(); }} else if(n=='2'){ action_ar.innerHTML= '<p><b>'+ key1 +'</b> のデータ(値)を <b>'+ key2 +'</b> にコピーします</p>'+ '<p>元の <b>'+ key2 +'</b> は <b>'+ next_key(key2) +'</b> として保存します '+ '<input class="do_action" value="コピー" type="submit">'+ '<input class="no_action" value="キャンセル" type="submit"></p>'+ '<style>'+ style_ac +'</style>'; action_color(key1); action_color(key2); let do_=action_ar.querySelector('.do_action'); do_.onclick=function(){ readers_value=read_local(key2); write_local(next_key(key2)); readers_value=read_local(key1); write_local(key2); window.location.reload(true); } let no_=action_ar.querySelector('.no_action'); no_.onclick=function(){ action_clear(); action_ar.remove(); }} }} // edit_key() function action_color(key){ let tr=document.querySelectorAll('#storage_man tr'); for(let k=1; k<tr.length; k++){ if(tr[k].querySelector('.td1').textContent==key){ tr[k].querySelector('.td2').style.background='aliceblue'; }}} function action_clear(){ let tr=document.querySelectorAll('#storage_man tr'); for(let k=1; k<tr.length; k++){ tr[k].querySelector('.td1').style.background=''; tr[k].querySelector('.td2').style.background=''; }} function next_key(key){ let numbers=[]; // 振り連番の配列 let key_; // 連番の基 let key_arr=key.split('_'); key_=key_arr[0] +'_'+ key_arr[1]; for(let i=0; i<localStorage.length; ++i){ //「AR_UserID」のkey名のストレージ登録を表示 if(localStorage.key(i).startsWith(key_)){ let num=localStorage.key(i).split('_')[2]; if(num){ numbers.push(num); }}} let empty; // 空番を探す変数 for(let k=0; k<100; k++){ let ks=k.toString(); if(!numbers.includes(ks)){ empty=ks; break; }} return key_arr[0] +'_'+ key_arr[1] +'_'+ empty; }