お知らせ

現在サイトのリニューアル作業中のため、全体的にページの表示が乱れています。
投稿日:
OS::Linux::コマンド

コマンド体系が独特なので毎回忘れる。使い方はman findで出す。find --helpしてもマニュアルは出てこない

目的 コマンド
通常検索 find . -name "*hoge"
正規表現検索 `find . -type f

typeで指定できる内容は以下の通り

type 内容
b block (buffered) special
c character (unbuffered) special
d directory
p named pipe (FIFO)
f regular file
l symbolic link
s socket
D door (Solaris)

正規表現で検索するオプションもあるようだが、直感的でないのでgrepで絞ったほうが早い。

生のHTTPメッセージを見たい時に使える方法。devtoolsではフォーマットされたログしか見れないが、生のテキストを見たい時に使える。具体的には以下の画像のような奴だ。

image-1706238902445.png

よく忘れるのでメモとして残しておく

確認環境

Env Ver
Google Chrome 120.0.6099.225
Microsoft Edge 120.0.2210.144

やり方

chrome://net-internals/にアクセスするとログ収集ができるので、ログを集めたらイベントビューワで見ればよい。なおイベントビューワは外部サイトなので注意。

以前はブラウザ内で完結していて、リアルタイムに見れたはずだが、何故かできなくなっていた。

Edgeの場合はedge://net-internals/でもアクセスできるが、特にこだわりがなければ汎用性の高いchrome://net-internals/でよいと思う。

投稿日:
言語::JavaScript言語::HTML

adiaryの改造でやったのでメモがてら

環境

Env Ver
Microsoft Edge 120.0.2210.144
PHP 8.2.10

サンプルコード

FORM形式での送信

Web標準でサポートされているため実装が非常に容易。特に理由がなければこれでいい

HTMLコード

<textarea onpaste="handlePasteForm(event)"></textarea>

JSコード

fetch()のヘッダ指定がないが、 fetch()を使う場合はFormDataを使うと勝手に生えるので気にしなくていい。他にもXMLHttpRequest.send()Navigator.sendBeacon()でも生えるらしい。

今回試したEdgeではバウンダリー文字列もちゃんと生えていた。

/** @param {ClipboardEvent} e */
const handlePasteForm = (e) => {
  if (e.clipboardData.files.length) {
    const pasteFile = e.clipboardData.files[0];
    const form = new FormData();
    form.append('image', pasteFile);

    fetch('./test.php', {
      method: 'POST',
      body: form
    });
  } else {
    // 何もしない
  }
};

PHPコード

Content-Typemultipart/form-dataの時だけ中身が入ってくる
参考:https://www.php.net/manual/ja/reserved.variables.files.php

<?php
$file_name = $_FILES['image']['name'];
move_uploaded_file($_FILES['image']['tmp_name'], './'. $file_name);
>

$_FILESの中身はvar_dumpした限りこんな感じだった

array(1) {
  ["image"]=>
  array(6) {
    ["name"]=>
    string(9) "image.png"
    ["full_path"]=>
    string(9) "image.png"
    ["type"]=>
    string(9) "image/png"
    ["tmp_name"]=>
    string(29) "C:\env\msys64\tmp\php3B90.tmp"
    ["error"]=>
    int(0)
    ["size"]=>
    int(3988)
  }
}

JSON形式での送信

Web標準ではないので、これといったやり方もなく、正直面倒だが、どうしてもJSONで送らないといけないときに。

HTMLコード

<textarea onpaste="handlePasteJson(event)"></textarea>

JSコード

HTTPではバイナリを送ることができないため、Base64にデコードして送る。Base64デコードに含まれる情報はファイルバイナリのみ。もしFORM送信のようにファイル名も送りたければ、別途送ってやる必要がある。

/** @param {File} file */
const encodeBase64 = (file) => {
  return new Promise ((resolve, reject) => {
    const fr = new FileReader();

    fr.onload = () => {
      const data = fr.result.split(',')[1];
      resolve(fr.result.split(',')[1]);
    };

    fr.onerror = (ev) => {
      reject(ev);
    };

    fr.readAsDataURL(file);
  });
};

/** @param {ClipboardEvent} e */
const handlePasteJson = async (e) => {
  if (e.clipboardData.files.length) {
    const pasteFile = e.clipboardData.files[0];

    const base64File = await encodeBase64(pasteFile);

    fetch('./test.php', {
      method: 'POST',
      headers: {
        'content-type': 'application/json'
      },
      body: JSON.stringify({ image: base64File })
    });
  } else {
    // 何もしない
  }
};

PHPコード

JSONリクエストを取るときはfile_get_contents('php://input')を利用する。$_POSTContent-Typeapplication/x-www-form-urlencodedないしmultipart/form-data時しか取れないからだ。つまりFORMリクエスト以外はfile_get_contents('php://input')で取ると覚えておけばよいだろう。
参考:https://www.php.net/manual/ja/wrappers.php.php#wrappers.php.input

以下のコードはかなりいい加減なので余りアテにしてはいけない(特に拡張子取得の下りがガバガバすぎる)が、取り合えずpngかjpegかgifが送られてきた場合は動いてくれる筈だ。

<?php
// 連想配列にするため、json_decode()の第二引数をtrueにする(指定しないとstdClassになる
$json = json_decode(file_get_contents('php://input'), true);
// Base64エンコードを解除する
$file = base64_decode($json['image']);

// 一時ファイルに落とす
file_put_contents('json_upload', $file);
// 拡張子判別
$mime = mime_content_type('json_upload');
// 拡張子取得
$ext = explode('/', $mime);

// 拡張子付きにリネーム
rename('json_upload', 'image.'. $ext[1]);

参考

JSONでファイルを投げる方式での実装状況については以下の記事が参考になる。

WebAPI でファイルをアップロードする方法アレコレ #API - Qiita

一気通貫で動くサンプルコード

今までの実装が全部動かせるサンプル

<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
  if(preg_match('/^multipart\/form-data;/', $_SERVER['CONTENT_TYPE'])) {
    // form
    $file_name = $_FILES['image']['name'];
    move_uploaded_file($_FILES['image']['tmp_name'], './'. $file_name);
  } else {
    // json
    $json = json_decode(file_get_contents('php://input'), true);
    $file = base64_decode($json['image']);

    file_put_contents('json_upload', $file);
    $mime = mime_content_type('json_upload');
    $ext = explode('/', $mime);

    rename('json_upload', 'image.'. $ext[1]);
  }
} else {
?>

<!DOCTYPE html>
<html>
<head>
  <meta charset='utf-8'>
  <meta http-equiv='X-UA-Compatible' content='IE=edge'>
  <title>Upload test</title>
  <meta name='viewport' content='width=device-width, initial-scale=1'>
  <script>
    /** @param {File} file */
    const encodeBase64 = (file) => {
      return new Promise ((resolve, reject) => {
        const fr = new FileReader();

        fr.onload = () => {
          const data = fr.result.split(',')[1];
          resolve(fr.result.split(',')[1]);
        };

        fr.onerror = (ev) => {
          reject(ev);
        };

        fr.readAsDataURL(file);
      });
    };

    /** @param {ClipboardEvent} e */
    const handlePasteForm = (e) => {
      if (e.clipboardData.files.length) {
        const pasteFile = e.clipboardData.files[0];
        const form = new FormData();
        form.append('image', pasteFile);

        fetch('./test.php', {
          method: 'POST',
          body: form
        });
      } else {
        // 何もしない
      }
    };

    /** @param {ClipboardEvent} e */
    const handlePasteJson = async (e) => {
      if (e.clipboardData.files.length) {
        const pasteFile = e.clipboardData.files[0];

        const base64File = await encodeBase64(pasteFile);

        fetch('./test.php', {
          method: 'POST',
          headers: {
            'content-type': 'application/json'
          },
          body: JSON.stringify({ image: base64File })
        });
      } else {
        // 何もしない
      }
    };
  </script>
</head>
<body>
  <div>Form:<textarea onpaste="handlePasteForm(event)"></textarea></div>
  <div>Json:<textarea onpaste="handlePasteJson(event)"></textarea></div>
</body>
</html>

<?php
}
?>

adiaryは高速に動作し、検索機能やタグツリーがすこぶるいい。そこはいいのだが、MarkdownのパーサーがMarkdown.plをベースに作られているようで、個人的に合わなかった。

また投稿画面もシンプルでいいのだが、膨大にタグがある場合設定に非常に苦労するUIになっていて、これもイマイチだ。

タグが多すぎると探すのが大変

他にも見栄えの問題がある。PCとSPのUIをサーバーサイドで切り替えるというかなり古典的な実装になっていて、ここを潰すだけなら簡単なのだが、全体的にレスポンシブにするにはフルスクラッチで書き直す必要があり、これが非常に骨が折れそうだ。WordPressのテーマを一本書く並みの労力はかかるのでプロのデザイナーに頼めば100万以上はするだろう。

まだ他にも画像に設定されるAlt属性の値がおかしいなど、様々な問題があり今は一旦採用を見送ることにした。

これらを解決するためにはGFMベースのMarkdownパーサーを書き、タグの絞り込みUIを作り、レスポンシブテーマをフルスクラッチで書き、A11y周りの実装を直す必要があるのだが、全部やっていたら一年は掛かるだろうし、正直きついなと思ってしまった。

何より実装言語がPerlというのも厳しさを加速させている。幸いadiaryのコードは比較的奇麗なので、手の入れようはあるのだが、余りにもやることのボリュームが大きすぎる。最低でもMarkdownパーサーとUIとA11yだけは何とかしたいが、そこに注力するほど気力が持つかも怪しいので、ここは努力目標としたい。

因みに現状のサイトhttps://test.lycolia.infoで公開しているが、いったん整備が終わるまで更新はしないつもりだ。途中で飽きて消す可能性すらある。

Markdownパーサーについては、今WordPressで使っているものにもバグがあり、結構運用でカバーしている部分があるので、せめてここだけでもちゃんと作ることができればadiaryに移行してもいいかもしれない。いやでも、テーマも作りたいかも…。

正直今のWordPressサイトを90点とするとadiaryは87点くらいで、かなり惜しい感じなのだ。なお、改造はGitHubのリポジトリをフォークして行っている。

2024/01/29追記

MarkedでMarkdown部分をパースすればうまくいくかと思ったが、さつき記法との兼ね合いで、そう単純ではなかった。ToCと画像挿入、注釈周りがどうにかできればワンチャンありそうな気もするが、adiary本体の処理がかなり密結合してるので骨が折れそうだ。

MarkedにMarkdownをパースさせてやるだけのテキストDBブログならフルスクラッチで自作も考えたものの、adiaryがかなりパフォーマンスに固執した書き方をしているらしいので、どっちかといえばadiaryのMarkdownパーサーを何とか自分好みに改編して解決したい気がした。Marked互換ならとりあえず満足であるが、adiaryの注釈の仕組みはMarkedより優れている部分もあるので、この辺りを取り入れたいとかいろいろあり、前途は多難そうだ。

ただ正直後方互換をあんまり重視する気がないWordPressのアップデート体制や、プラグインやテーマの謎アップデートにも辟易しているところはあるので、乗り換えたいような気もするし、微妙な気持ちである。

adiaryという和製CMSがある。昔どこかで見たような懐かしい見た目のCMSだ。

書き口ははてなブログが近い。あちらは、はてな文法とMarkdownが使えるように、こちらはさつき文法とMarkdownが使える。

お試しで簡単に作ってみて、こちらのブログの記事をある程度移植してみたのだが、使い勝手もいい。

試しに作ってみたもの

まずサイドバーのタグツリーだ。WordPressではこんなものは作れないし、ツリーのタグをクリックすると記事の一覧が取れて更にそこからフィルタをかけることができる。

サイドバーのタグツリーからタグを選ぶと、更に絞り込みをかけることができる

正直言ってこれはかなり魅力的だと思う。WordPressの検索プラグインは使い勝手が悪いものばかりで、ここまで本格的なものは私は知らない。これはブログをログ代わりに使っている私にとって非常に魅力的な機能だ。

しかも表示速度も速い。以下は測定サイトで比較した結果だが、adiaryの速さは明らかだ。というか普通に操作していても全然違う。

WordPress adiary
WordPressの表示速度1
adiaryの表示速度1
WordPressの表示速度2
adiaryの表示速度2

この圧倒的な速度差を見るとadiaryに惹かれてしまう。ぶっちゃけ今すぐにでも乗り換えたいぐらいだ。

しかし、個人的にはいくつか物足りない部分がある。それはadiaryがGitHub Flavor Markdownをそんなにサポートしていないことや、A11yやレスポンシブデザインへの対応状況が今一つなところだ。この辺りはリポジトリをフォークしたので時間があるときに自分好みにカスタマイズしていきたいと思う。

とはいえ、WordPressもそれはそれで捨てがたいところがあり、悩むところである…。あとあちらはプレビュー機能がないので書きづらい(WordPressのエディタと違ってチープなのでスマホで書くときは便利そうだが

しかしMarkdownさえ快適に書ければ今にでも乗り換えたいので、やはり乗り換えたい気がする。というか乗り換えないと一生手出ししない気もするしなぁ…。とはいえ表示バグってるしなぁ…(記事として一部読めないのがある