ハッシュの配列をユニークにするPerlモジュール作りました
はい、前回言ってたモジュール公開しました。
metacpan.org
CPAN Authorの登録や、minilコマンド使ってのモジュール公開など、
初めてでしたが比較的やりやすかったです^^
ただ何箇所か詰まった部分もあるので、そこはPerl入学式の方に聞いて助けてもらいました。ご協力ありがとうございました!
なお、「Hash::Uniqueという名前は空間名を占有しすぎ」というご指摘もいただいたので、次回からは気をつけたいと思います。
モジュール自体は、もともとPHPで作ってた関数をPerlに移植した形です。
連想配列(Perlではハッシュの配列)を、指定したキーで一意にするという関数です。
まあ、同じようなモジュールは既にCPANにありそうですね^^;
公開したモジュールのソースはCPANから見れるので、ここでは元となったphpのソースを載せておきます。
<?php function getUniqueAssociativeArray($arr, $key){ $tmp = array(); $resultArr = array(); foreach( $arr as $value ){ if(!in_array( $value[$key], $tmp)) { array_push($tmp, $value[$key]); array_push($resultArr, $value); } } return $resultArr; } $people = array( array('id' => 1, 'name' => '佐藤'), array('id' => 2, 'name' => '鈴木'), array('id' => 3, 'name' => '田中'), array('id' => 4, 'name' => '佐藤') ); $peopleUnique = getUniqueAssociativeArray($people, "name"); var_dump($peopleUnique); ?>
結果
array(3) { [0]=> array(2) { ["id"]=> int(1) ["name"]=> string(6) "佐藤" } [1]=> array(2) { ["id"]=> int(2) ["name"]=> string(6) "鈴木" } [2]=> array(2) { ["id"]=> int(3) ["name"]=> string(6) "田中" } }
Netbeansの▶︎ボタンでphpファイルをコマンド実行する
バッチ処理書くときとか、いちいちコマンドライン立ち上げて実行するのも面倒なので、いつもこうやって動作確認してますよ、という話。
① プロジェクトの構成を設定
まずNetBeansを立ち上げて、「実行」から「プロジェクトの構成を設定」を選びます。
最初は「デフォルト」にチェックが入っているはずです。ここでは「カスタマイズ」を選びましょう。
② 新規構成を作成
構成は「デフォルト」になっているので、「新規」ボタンをクリックしましょう。
構成名は任意でわかりやすい名前を入れればOKです。
⑥ ▶︎ボタンで実行
地球マークの左側のプルダウンから、作成した構成を選択できます。
選択後、緑の▶︎ボタンをクリックすれば実行されます。
実行結果は、下部の出力ウィンドウに出力されます。
jsとcanvasで画像の容量を落とす〜その2〜
前回のだと上手くcanvasに描画されない場合が多かったので、やり直してみました。
htmlはこちら。
<form action="" method="post" id="imageForm"> <img src="" id="preview" /> <canvas id="canvas"></canvas> <input type="file" id="imageSelect" onChange="imgDisp();" /> <input type="button" onClick="imgUpload();" value="アップロード" /> </form>
まず、ファイル選択時の処理。
選択した画像を、img要素に原寸サイズで表示します。
原寸サイズで表示して全体のレイアウト崩れるの嫌なら、hiddenにすればOKです。
function imgDisp() { var file = $("#imageSelect").prop("files")[0]; //画像ファイルかチェック if (file["type"] != "image/jpeg" && file["type"] != "image/png" && file["type"] != "image/gif") { alert("jpgかpngかgifファイルを選択してください"); $("#imageSelect").val(''); return false; } var fr = new FileReader(); fr.onload = function() { //選択した画像をimg要素に表示 $('#preview').attr("src", fr.result); }; fr.readAsDataURL(file); }
次に、アップロードボタンクリック時の処理。
img要素から画像をimageオブジェクトとして取得して、canvas要素に描画します。
描画時にサイズ縮小すると、これだけで結構容量下がります。
今回は描画時の横幅を800pxにして、縦横比保ったまま縮小してます。
あ、ちなみにcanvas要素もhidden入れたら非表示にできます。
そして、canvasデータの画質を落としてajaxでPOST送信します。
ここではtoDataURLでバイナリ化しますが、この時に第二引数で画質が指定できるんです。
0.0〜1.0の数値を指定して、好きな画質に落としましょう。
今回は、100KB以下に落とします。
まず最初は、第二引数を指定せずオリジナル容量(画質を落としていない場合の容量)を取得します。
次に、取得したオリジナル容量から100KB以下に落とすための数値を算出して、指定しています。
最後に、ajaxでPOST送信したら完了!
function imgUpload() { //加工後の横幅を800pxに設定 var processingWidth = 800; //加工後の容量を100KB以下に設定 var processingCapacity = 100000; //ファイル選択済みかチェック var fileCheck = $("#imageSelect").val().length; if (fileCheck === 0) { alert("画像ファイルを選択してください"); return false; } //imgタグに表示した画像をimageオブジェクトとして取得 var image = new Image(); image.src = $("#preview").attr("src"); var h; var w; //原寸横幅が加工後横幅より大きければ、縦横比を維持した縮小サイズを取得 if(processingWidth < image.width) { w = processingWidth; h = image.height * (processingWidth / image.width); //原寸横幅が加工後横幅以下なら、原寸サイズのまま } else { w = image.width; h = image.height; } //取得したサイズでcanvasに描画 var canvas = $("#canvas"); var ctx = canvas[0].getContext("2d"); $("#canvas").attr("width", w); $("#canvas").attr("height", h); ctx.drawImage(image, 0, 0, w, h); //canvasに描画したデータを取得 var canvasImage = $("#canvas").get(0); //オリジナル容量(画質落としてない場合の容量)を取得 var originalBinary = canvasImage.toDataURL("image/jpeg"); //画質落とさずバイナリ化 var originalBlob = base64ToBlob(originalBinary); //画質落としてないblobデータをアップロード用blobに設定 console.log(originalBlob["size"]); //オリジナル容量blobデータをアップロード用blobに設定 var uploadBlob = originalBlob; //オリジナル容量が加工後容量以上かチェック if(processingCapacity <= originalBlob["size"]) { //加工後容量以下に落とす var capacityRatio = processingCapacity / originalBlob["size"]; var processingBinary = canvasImage.toDataURL("image/jpeg", capacityRatio); //画質落としてバイナリ化 uploadBlob = base64ToBlob(processingBinary); //画質落としたblobデータをアップロード用blobに設定 console.log(capacityRatio); console.log(uploadBlob["size"]); } //アップロード用blobをformDataに設定 var form = $("#imageForm").get(0); var formData = new FormData(form); formData.append("selectImage", uploadBlob); //formDataをPOSTで送信 $.ajax({ async: false, type: "POST", url: "upload.php", data: formData, dataType: "text", cache: false, contentType: false, processData: false, error: function (XMLHttpRequest) { console.log(XMLHttpRequest); alert("アップロードに失敗しました"); }, success: function (res) { if(res !== "OK") { console.log(res); alert("アップロードに失敗しました"); } else { alert("アップロードに成功しました"); } } }); } // 引数のBase64の文字列をBlob形式にする function base64ToBlob(base64) { var base64Data = base64.split(',')[1], // Data URLからBase64のデータ部分のみを取得 data = window.atob(base64Data), // base64形式の文字列をデコード buff = new ArrayBuffer(data.length), arr = new Uint8Array(buff), blob, i, dataLen; // blobの生成 for (i = 0, dataLen = data.length; i < dataLen; i++) { arr[i] = data.charCodeAt(i); } blob = new Blob([arr], {type: 'image/jpeg'}); return blob; }
upload.phpは、シンプルにPOSTで受けた画像をアップロードしてるだけです。
バイナリ化するときにjpgで指定してるので、保存するファイルの拡張子はjpg固定です。
<?php try{ if(!move_uploaded_file($_FILES["selectImage"]["tmp_name"], 'test.jpg')){ throw new Exception('画像ファイルアップロードエラー!'); } echo 'OK'; } catch (Exception $ex) { echo $ex->getMessage(); } ?>
という訳で前回からの大きな変更は、canvas描画のタイミングをファイル選択時からアップロードボタンクリック時に移したことです。
どうやらinput type="file" からimg要素への表示と、img要素からcanvasへの描画は、分けて処理しないと上手くいかない模様。
FileReaderオブジェクトの、onload プロパティとreadAsDataURL メソッドの使い方なのかなあ…
でも、onload プロパティとreadAsDataURL メソッドの後に、canvas描画やっても一緒だったしなあ。。。
ちなみに、バリデーション的にpngもOKにはしてますが、jpgに変換してるので透過部分は黒くなってしまいます^^;
次回はこの辺解決してみるのもありかな。
<参照>
‘input type=file’から’canvas’への転写(画像の引き伸ばし対処) – GUNMA GIS GEEK
toDataURL() メソッド - Canvasリファレンス - HTML5.JP
canvasで描いた絵をバイナリ形式でサーバーにPOST送信する方法 | while(isプログラマ)
【修正あり】jsとcanvasで画像の容量を落とす
【2017/5/1】
修正加えたその2書きました。
matz.hatenablog.jp
【2017/4/2修正】
phpのソース載せてなかったので、追記しました。
phpで画像アップロードしたいけど、最近のiPhoneのカメラとか画質良いので容量オーバーしちゃいますよね。
まあphp.iniで「post_max_size」とか触ればいいんですけど、なんせ重いです。
それなら、クライアントで容量落としてから上げれば良いじゃん!ってことでこちらの方法!
htmlはこんな感じです。
<form action="" method="post" id="imageForm"> <img src="" id="preview" /> <canvas id="canvas"></canvas> <input type="file" id="imageSelect" onChange="canvasDraw();" /> <input type="button" onClick="imageUpload();" value="アップロード" /> </form>
まず、input type="file"で選択した画像を、一旦img要素に原寸サイズで表示します。
原寸サイズで表示して全体のレイアウト崩れるの嫌なら、hiddenにすればOKです。
次に、img要素から画像をimageオブジェクトとして取得して、canvas要素に描画します。
描画時にサイズ縮小すると、これだけで結構容量下がります。
今回は横幅800px固定にして、縦横比保ったまま縮小してます。
ちなみに、canvas要素もhidden入れたら非表示にできます。
function canvasDraw() { var file = $("#imageSelect").prop("files")[0]; //画像ファイルかチェック if (file["type"] != "image/jpeg" && file["type"] != "image/png" && file["type"] != "image/gif") { alert("画像ファイルを選択してください"); $("#imageSelect").val(''); //選択したファイルをクリア } else { var fr = new FileReader(); fr.onload = function() { //選択した画像を一旦imgタグに表示 $("#preview").attr('src', fr.result); //imgタグに表示した画像をimageオブジェクトとして取得 var image = new Image(); image.src = $("#preview").attr('src'); //縦横比を維持した縮小サイズを取得 var w = 800; var ratio = w / image.width; var h = image.height * ratio; //canvasに描画 var canvas = $("#canvas"); var ctx = canvas[0].getContext('2d'); $("#canvas").attr("width", w); $("#canvas").attr("height", h); ctx.drawImage(image, 0, 0, w, h); }; fr.readAsDataURL(file); } }
そして、アップロードボタンクリックしたタイミングで、canvasデータの画質を落としてajaxでPOST送信します。
ここではtoDataURLでバイナリ化しますが、この時に第二引数で画質が指定できるんです。
0.0〜1.0の数値を指定して、好きな画質に落としましょう。
今回は、2MB以下に落とします。
まず最初は、第二引数を指定せずオリジナル容量(画質を落としていない場合の容量)を取得します。
次に、取得したオリジナル容量から2MB以下に落とすための数値を算出して、指定しています。
最後に、ajaxでPOST送信したら完了!
function imageUpload() { var form = $("#imageForm").get(0); var formData = new FormData(form); //画像処理してformDataに追加 if ($("#canvas").length) { //canvasに描画したデータを取得 var canvasImage = $("#canvas").get(0); //オリジナル容量(画質落としてない場合の容量)を取得 var originalBinary = canvasImage.toDataURL("image/jpeg"); //画質落とさずバイナリ化 var originalBlob = base64ToBlob(originalBinary); //オリジナル容量blobデータを取得 console.log(originalBlob["size"]); //オリジナル容量blobデータをアップロード用blobに設定 var uploadBlob = originalBlob; //オリジナル容量が2MB以上かチェック if(2000000 <= originalBlob["size"]) { //2MB以下に落とす var capacityRatio = 2000000 / originalBlob["size"]; var processingBinary = canvasImage.toDataURL("image/jpeg", capacityRatio); //画質落としてバイナリ化 uploadBlob = base64ToBlob(processingBinary); //画質落としたblobデータをアップロード用blobに設定 console.log(capacityRatio); console.log(uploadBlob["size"]); } //アップロード用blobをformDataに追加 formData.append("selectImage", uploadBlob); } //formDataをPOSTで送信 $.ajax({ async: false, type: "POST", url: "upload.php", data: formData, dataType: "text", cache: false, contentType: false, processData: false, error: function (XMLHttpRequest) { console.log(XMLHttpRequest); alert("アップロードに失敗しました"); }, success: function (res) { if(res !== "OK") { console.log(res); alert("アップロードに失敗しました"); } else { alert("アップロードに成功しました"); } } }); } // 引数のBase64の文字列をBlob形式にする function base64ToBlob(base64) { var base64Data = base64.split(',')[1], // Data URLからBase64のデータ部分のみを取得 data = window.atob(base64Data), // base64形式の文字列をデコード buff = new ArrayBuffer(data.length), arr = new Uint8Array(buff), blob, i, dataLen; // blobの生成 for (i = 0, dataLen = data.length; i < dataLen; i++) { arr[i] = data.charCodeAt(i); } blob = new Blob([arr], {type: 'image/jpeg'}); return blob; }
upload.phpは、シンプルにPOSTで受けた画像をアップロードしてるだけです。
バイナリ化するときにjpgで指定してるので、保存するファイルの拡張子はjpg固定です。
<?php try{ if(!move_uploaded_file($_FILES["selectImage"]["tmp_name"], 'test.jpg')){ throw new Exception('画像ファイルアップロードエラー!'); } echo 'OK'; } catch (Exception $ex) { echo $ex->getMessage(); } ?>
なので、この方法だと出来上がる画像はjpg固定になってしまいます。
pngやgifにする方法あれば教えてくださいm(_ _)m
<参照>
‘input type=file’から’canvas’への転写(画像の引き伸ばし対処) – GUNMA GIS GEEK
toDataURL() メソッド - Canvasリファレンス - HTML5.JP
canvasで描いた絵をバイナリ形式でサーバーにPOST送信する方法 | while(isプログラマ)