今回は、画像ファイルをそのままアップロードするのではなくリサイズしてからアップロードする方法を考えます。
前提として、ファイルをアップロードするだけなら以下の方法でできます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>画像をアップロードする</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> </head> <body> <form action="upload.php" enctype="multipart/form-data" method="post"> <input name="file_upload" type="file"> <input type="submit" value="アップロード"> </form> </body> </html> |
upload.php
1 2 3 4 5 6 7 8 |
<?php $upload = './'.$_FILES['file_upload']['name']; if(move_uploaded_file($_FILES['file_upload']['tmp_name'], $upload)) echo 'アップロード完了'; else echo 'アップロード失敗'; ?> |
Contents
HTML部分
今回はそのままアップロードするのではなくリサイズしてからアップロードする方法を考えます。
HTML部分を示します。
ファイルが選択されたらcanvasに画像を描画します。そしてアップロードボタンがクリックされたらcanvasに描画されているものをアップロードします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>サイズ調整した画像をアップロードする</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> </head> <body> <input type="radio" name="option1" value="0" id = "r0" onchange="radioChangeed()"> <label for="r0">調整なし(はみ出した部分はカット)</label><br> <input type="radio" name="option1" value="1" id = "r1" onchange="radioChangeed()"> <label for="r1">サイズを320×320に自動調整(縦横の比率無視)</label><br> <input type="radio" name="option1" value="2" id = "r2" onchange="radioChangeed()"> <label for="r2">サイズを320×320に自動調整(縦横の比率維持)</label><br> <p><input id ="file-name" type="file"></p> <p><button onclick="upload()">アップロード</button></p> <p><canvas id = "canvas"></canvas></p> <div id = "result"></div> <script src="./index.js"></script> </body> </html> |
JavaScript部分
主なグローバル変数と定数は以下のとおりです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
const WIDTH = 320; const HEIGHT = 320; const $fileName = document.getElementById('file-name'); const $canvas = document.getElementById('canvas'); $canvas.width = WIDTH; $canvas.height = HEIGHT; const ctx = $canvas.getContext('2d'); let file = null; const $image = new Image(); let isImage = false; // 選択されているファイルは画像ファイルか? let drawOption = 0; /* 0: 調整なし(はみ出した部分はカット) 1: サイズを320×320に自動調整(縦横の比率無視) 2: サイズを320×320に自動調整(縦横の比率維持) */ const $result = document.getElementById('result'); |
ページが読み込まれたときの処理
ページが読み込まれたらラジオボタンの一番上が選択されている状態にします。
1 2 3 4 5 6 7 |
window.onload = () => { const $r0 = document.getElementById('r0'); $r0.checked = 'checked'; drawOption = 0; drawImage(); // 後述 } |
選択されているファイルが変更されたときの処理
選択されているファイルが変更されたらcanvasに画像を描画します。このとき画像でないものを選択するとエラーが発生するので、このときはisImageフラグをfalseにしてcanvasにはなにも描画しないことにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
$fileName.addEventListener('change', () => { file = $fileName.files[0]; let fileReader = new FileReader(); fileReader.readAsDataURL(file); fileReader.onloadend = () => { $image.src = fileReader.result; $image.onload = () => { isImage = true; drawImage(); // 後述 } $image.onerror = () => { isImage = false; drawImage(); } }; }); |
canvasに画像を描画する処理
canvasに画像を描画する処理を示します。
isImageフラグをチェックしてtrueであればdrawOptionによって画像をそのままのサイズで、またはサイズ変更して描画処理をおこない、falseの場合は黒で塗りつぶす処理だけをおこないます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
function drawImage(){ ctx.fillStyle = '#000'; ctx.fillRect(0, 0, $canvas.width, $canvas.height); if(!isImage) return; if(drawOption == 0) // サイズ変更なし ctx.drawImage($image, 0, 0); if(drawOption == 1) // 幅高さの長さの比率無視でcanvasのサイズに拡大縮小して描画処理 ctx.drawImage($image, 0, 0, $canvas.width, $canvas.height); if(drawOption == 2){ // 幅高さの長さの比率を維持してcanvasのサイズに拡大縮小して描画処理 // canvasのサイズに合わせるのは幅か高さか? // 縮小した場合、中心に描画されるようにctx.drawImage関数の第二、第三引数を変更する if($image.width > $image.height){ let newHeight = $image.height * WIDTH / $image.width; ctx.drawImage($image, 0, (HEIGHT - newHeight) / 2, WIDTH, newHeight); } else { let newWidth = $image.width * HEIGHT / $image.height; ctx.drawImage($image, (WIDTH - newWidth) / 2, 0, newWidth, HEIGHT); } } } |
ラジオボタンの状態が変化したらどれが選択されているかを調べて、適切な状態で画像を描画しなおします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
function radioChangeed(){ const $r0 = document.getElementById('r0'); const $r1 = document.getElementById('r1'); const $r2 = document.getElementById('r2'); if($r0.checked) drawOption = 0; if($r1.checked) drawOption = 1; if($r2.checked) drawOption = 2; drawImage(); } |
canvasに描画されている画像をアップロードする
アップロードボタンがクリックされたら画像ファイルが選択されている場合はcanvasに描画されている画像をサーバーに送信します。upload.phpですが、冒頭のものをそのまま使ってもよいし、node.js + PHP ユーザーがアップロードした不適切な画像を削除するの最後のほうに示しているものを使えば不適切な画像のみ自動で削除することもできます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
async function upload(){ $canvas.style.display = 'none'; // アップロードの結果表示でも画像が表示されるのでcanvasは非表示に // 問題がある場合はエラーメッセージを表示 if($fileName.files.length == 0){ $result.innerHTML = '<p>ファイルが選択されていません</p>'; $result.style.display = 'block'; return; } if(!isImage){ $result.innerHTML = '<p>このファイルは画像ファイルではないのでアップロードできません</p>'; $result.style.display = 'block'; return; } // canvasに描画されている画像からfileオブジェクトを生成 const dataURL = $canvas.toDataURL(); const imgFile = convertToFile (dataURL, file); // 送信用のフォームを生成 const fd = new FormData(); fd.append('file_upload', imgFile); // 送信 const rosponse = await fetch('./upload.php', { method: 'POST', body: fd, }); // 送信したら結果を表示 const html = await rosponse.text(); const dom = new DOMParser().parseFromString(html, 'text/html'); $result.innerHTML = dom.body.innerHTML; $result.style.display = 'block'; } // dataURLをファイルオブジェクトに変換する function convertToFile (dataURL, file) { const blob = atob(dataURL.replace(/^.*,/, '')); const arr = new Uint8Array(blob.length); for (let i = 0; i < blob.length; i++) arr[i] = blob.charCodeAt(i); return new File([arr.buffer], file.name, {type: file.type}); } |
また画像ファイルを選択するときは非表示になっているcanvasを再表示させ、アップロードの結果を表示している要素を非表示にします。
1 2 3 4 5 6 7 |
$fileName.addEventListener('click', () => { $canvas.style.display = 'block'; $result.style.display = 'none'; ctx.fillStyle = '#000'; ctx.fillRect(0, 0, $canvas.width, $canvas.height); }); |