背景
laravel アプリケーションで、画像アップロードする際に容量の問題でアップロードできない事象が発生。
画像アップロード時にフロント側で画像をリサイズする必要が出てきた。
画像リサイズ処理に canvas API なるものが使えそうだということがわかったので、
canvas 使って画像をリサイズする方針。
フロントフレームワークを使っていないので、bladeファイルにそのまま jquery を書く実装を選択。
ソースコード
複数枚画像をアップロードするケースを想定。
<input
type="file"
name="multipleImages[]"
class="form-control-file"
onchange="resize(event)"
accept=".jpg,.png,.jpeg,image/jpg,image/png,image/jpeg"
/>
function resize(event) {
let element = event.target
let uploadedFiles = element.files
let fileLength = element.files.length
for (i = 0; i < fileLength; i++) {
let image = new Image();
let reader = new FileReader();
let uploadedFile = uploadedFiles[i];
let canvas = document.createElement('canvas')
reader.onload = function (e) {
image.onload = function () {
const MAX_WIDTH = 4000;
const MAX_HEIGHT = 4000;
let width = image.width;
let height = image.height;
if (width > height) {
if (width > MAX_WIDTH) {
height *= MAX_WIDTH / width;
width = MAX_WIDTH;
}
} else {
if (height > MAX_HEIGHT) {
width *= MAX_HEIGHT / height;
height = MAX_HEIGHT;
}
}
canvas.width = width;
canvas.height = height;
canvas.name = uploadedFile.name
canvas.type = uploadedFile.type
canvas.size = uploadedFile.size
let ctx = canvas.getContext('2d')
ctx.drawImage(image, 0, 0, width, height)
canvasList.push(canvas)
if (canvasList.length == fileLength) {
const dataTransfer = new DataTransfer();
for (let i = 0; i < fileLength; i++) {
if (uploadedFiles[i].size >= 2000000) {
let blob = toBlob(canvasList[i])
let resizedFile = new File([blob], canvasList[i].name, {type: canvasList[i].type})
dataTransfer.items.add(resizedFile);
} else {
let originalFile = uploadedFiles[i]
dataTransfer.items.add(originalFile);
}
}
element.files = dataTransfer.files;
}
}
image.src = e.target.result;
}
reader.readAsDataURL(uploadedFile);
}
function toBlob(canvas) {
let base64 = canvas.toDataURL(canvas.type)
let bin = atob(base64.replace(/^.*,/, ''))
let buffer = new Uint8Array(bin.length);
for (var i = 0; i < bin.length; i++) {
buffer[i] = bin.charCodeAt(i);
}
return new Blob([buffer.buffer], { type: canvas.type })
}
ソースコード詳細
if (uploadedFiles[i].size >= 2000000) {
容量が2MB以上だとリサイズ画像、それ以外はオリジナル画像をアップロード対象にする
element.files = dataTransfer.files;
ajaxではなく、laravel標準のフォームから画像をアップロードしたかったので、
リサイズ後の画像を再度、input要素に格納する
参考
https://stackblitz.com/edit/react-canvas-image-resize?file=src%2FApp.js
https://qiita.com/ysks-y/items/086d7b5a4e18f0c27929