スマホが日進月歩の時代になりました。写真1枚で簡単に10Mbを超えるでしょう。サーバへ送信する前に、クライアント側でアップロードされたイメージをリサイズできれば、サーバに対する負荷を軽減できるし、アップロードスピードの向上するためユーザビリティ質にもつながります。なので、クライアントでのイメージリサイズは現代ウェブアプリケーションの必須機能ともいえるでしょう。
WebAPIを利用すれば、リサイズ自身は難しくありません。htmlのcanvas domからtoBlobでイメージのサイズと画質(quality)を調整するのが一般的です。
const canvasDom = document.createElement('canvas');
// quality → 0.9
const quality = 0.9;
// height, width → 50%
canvasDom.width = image.width / 2;
canvasDom.height = image.height / 2;
const ctx = canvasDom.getContext('2d');
ctx.drawImage(image, 0, 0, image.height / 2, image.width / 2);
canvasDom.toBlob(
function blobCallback(blob) {
uploadFile = new File([blob], "file-name.jpg");
},
type,
quality,
);
しかし、アップロードされたFile
からImage
へ変換し、そして、変換されたImage
をCanvas DOMに書き込めるかどうかはブラウザによって挙動が異なります。
自分がこれから紹介する3つの方法で検証してみました。
TLDR:動作がおかしいブラウザはiOSのSafariとMacOSのSafariでした。PCのChrome、FireFox、AndroidのChromeいずれもどの方法でも問題ありません。頑張って、Appleさん。
方法1:FileReader.readAsDataURL
Google先生に聞くと、この方法一番多いです。ただ、iOSとMacOS上にはとても不安定で、File
→Image
へ変換するとき、image.width
が0
になる可能性があるので、おすすめできません。
使い方は簡単です。 ※Promiseでラップしたらcallback地獄を避けられます。
// fileは選択されたファイル
const image = new Image();
await new Promise((resolve) => {
const reader = new FileReader();
reader.onload = function (e) {
image.src = e.target.result;
resolve();
};
reader.readAsDataURL(file);
});
// imageを使う
方法2:createImageBitmap
これは一番簡単だが、Safariは完全にアウトですね。
// fileは選択されたファイル
const image = createImageBitmap(file);
// imageを使う
方法3:URL.createObjectURL
この方法は今の時点で一番安定しています。
// fileは選択されたファイル
// `file`をobjectURLへ変換
const url = URL.createObjectURL(file);
// imageへ変換
const image = new Image();
img.decoding = 'async';
img.src = url;
await new Promise((resolve, reject) => {
img.onload = () => resolve();
img.onerror = () => reject(Error('Image loading error.'));
});
// imageを使う
完了
3つの方法の検証用のウェブサイト:https://ios-resize-image.vercel.app/