[Javascript] 画像のファイルタイプをバイナリデータから読み取る「filetype.js」#Nodejs版

2022年1月16日

Javascript テクノロジー

t f B! P L
eyecatch 便利にwebページを作るフレームワークを追求する、ユゲタです。 世の中には便利にホームページを作れるフレームワークがたくさんあふれていて、簡単に構築できるようになっていますが、 確かに便利だけど、無駄にフレームワーク内で覚えることが多く、学習コストが高かったり、 異常にサーバー負荷が高く、処理速度が遅かったり、データベースやら、サーバー環境、各種モジュールのバージョンなどが限定的になっていて、依存関係が激しいフレームワークが多く、 あまり効率のいいと思えるフレームワークには出会ったことがありません。

OSS立ち上げるよ

そんなワケで、NodejsベースのOSSプロジェクトとして発足しようとモックアップを作っている最中なんですが、 そのフレームワークシステムの中で、ファイルタイプを取得する処理があったんですが、ライブラリを使うよりも、自分で一回書いてみようということで、 「filetype.js」という処理を作ってみました。 通常、拡張子で確認すればいいだろうと思いがちですが、実はこのフレームワークでは、最終目標としてSEOの点数アップをさせる機能を入れようと思ってまして、画像はすべてwebpとして変換してしまう予定なんです。 しかし、ブログページのような任意のHTMLを書き込むような構造を持ってしまうと、imgタグにsrcをベタ書きしてしまって、ファイル拡張子がバッチリ固定化されてしまうため、その中身を全て変換していくというのも、なかなか処理速度を犠牲にする事になるので、 拡張子はそのままでwebp変換してしまおうという強制処理に対する対策だったんですね。 無謀に聞こえるかもしれませんが、これは、akamaiなどのCDNがやっている方法で、確かに利にはかなっている事がわかります。

拡張子が違う画像ってブラウザで正常に表示されるの?

ここで一つ心配なのは、jpgやpngという拡張子のまま、webpフォーマットに変換した時に、インターネットブラウザで正常に表示ができるかという問題がありますが、色々と実験してみたところ、ブラウザでは全く問題なく表示されるということがわかりました。 しかし、そのままでは、例えば、画像のサイズ変更をしなければいけないような処理の時に、多くのライブラリは拡張子で判断されてしまうので、このjpegファイルは中身はwebpだよというのを、システム内で特定できる仕組みが必要というワケなんですね。

ソース公開

世の中のファイルフォーマット全てを対応するのは、なかなかしんどそうなので、とりあえず、webで主要な画像フォーマットだけを対応するプログラムを書いてみました。 const fs = require("fs"); module.exports = { get_type : function(path){ if(!fs.existsSync(path)){return null;} const buffer = fs.readFileSync(path); // png if(this.check_png(buffer)){ return "png"; } // jpg else if(this.check_jpg(buffer)){ return "jpg"; } // gif else if(this.check_gif(buffer)){ return "gif"; } // webp else if(this.check_webp(buffer)){ return "webp"; } else{ return "--"; } }, check_png : function(buffer){ let head = ""; for(let i of buffer.slice(0,8)){ head += ("00"+i.toString(16)).slice(-2); } if(head.toLowerCase() === "89504e470d0a1a0a"){ return true; } }, check_jpg : function(buffer){ let head = ""; for(let i of buffer.slice(0,2)){ head += ("00"+i.toString(16)).slice(-2); } if(head.toLowerCase() === "ffd8"){ return true; } }, check_gif : function(buffer){ let head = ""; for(let i of buffer.slice(0,6)){ head += ("00"+i.toString(16)).slice(-2); } head = this.hex2a(head); switch(head.toLowerCase()){ case "gif87a": case "gif89a": return true; } }, check_webp : function(buffer){ let head = ""; for(let i of buffer.slice(0,12)){ head += ("00"+i.toString(16)).slice(-2); } if(head.toLowerCase() === "524946463a11000057454250"){ return true; } }, hex2a : function(hexx){ var hex = hexx.toString();//force conversion var str = ''; for (var i = 0; (i < hex.length && hex.substr(i, 2) !== '00'); i += 2) str += String.fromCharCode(parseInt(hex.substr(i, 2), 16)); return str; } }; filetype.jsというファイル名で保存をして、nodejs上で、 const type = require("filetype.js")("%ファイルパス"); とすると、typeに対して、"jpg","png","gif","webp"のどれかが返ってきます。 どれでもない時はnullまたはブランク文字列が返るので、!判定してあげるだけでいいでしょう。 ちなみに、この祖ソースでやっているのは、ファイルのバイナリデータのヘッダ部分を判定して、ファイルタイプを判別しているというやり方で行っています。 javascriptでバイナリ処理を行う人は少ないかと思いますが、この辺のやり方が気になる人は、ソースの中身をいじってみて学習してもらってもいいかもしれません。

最後に

現在OSSの立ち上げ真っ最中なんですが、こちらも興味のある人は是非こちらから、コメントを貰えると、お返事させていただきたいと思います。 このブログを通じて、中でどんどん作っているライブラリや、作っていて身についたスキルなどを公開していこうと思っています。 お楽しみに!

人気の投稿

このブログを検索

ごあいさつ

このWebサイトは、独自思考で我が道を行くユゲタの少し尖った思考のTechブログです。 毎日興味がどんどん切り替わるので、テーマはマルチになっています。 もしかしたらアイデアに困っている人の助けになるかもしれません。

ブログ アーカイブ