概要
Webアプリでクライアントサイド(ブラウザ)で画像を圧縮(リサイズ)する方法の紹介です。
サンプルとして下図のような入力した画像をプレビュー表示した上で、確定時にサーバーに画像をアップロードするまでのサンプルコードを紹介します。
※今回はvue.jsとvuetifyによるサンプルになりますが、UIのレイアウト部分以外は基本的に一般的なjavascirptですので
他のフレームワークを使用している場合等でも参考にはなるかと思います
動くデモとGitHubのサンプルコードは下記です。
Demo
GitHub
背景
Webアプリで、画像ファイルをアップロードするといったシチュエーションはよくあると思います。
スマホ等で撮影した写真等だと、解像度やファイルサイズも大きいため、そのまま使用せず一度リサイズしたりする事が多いかと思います。
アップロード後にサーバ側で処理するといった手法もありますが、今回はWEBブラウザ側でリサイズする方法とします。
実現方法
当初はCanvasにリサイズした画像を描画して、再度データ化する方法を使用していました。
参考
しかしながら、もっとお手軽にかつファイルサイズも小さくしてアップできる、Browser Image Compression といったものがあるのでそちらを使用します。
画面レイアウト
以下のような、3つの領域を作成します。
-
画像のプレビュー領域
入力した画像のプレビュー表示します。サンプルではvuetifyのv-imgタグを使用していますが、imgタグに相当するもの配置します。合わせてファイル名も表示します。 -
画像の入力
ファイルを入力するinputタグです。ファイル選択対アログで画像を選択するとプレビュー領域に表示されます。 -
Submitボタン
プレビュー表示した画像をサーバにアップロードするためのボタン。画像入力中(リサイズ中)はプレビュー表示されるまでは押せなくします。
これらをvue.jsのコンポーネントで書くと下記のようになります。
処理等は無く、まだレイアウトだけです。
<template>
<v-container>
<v-layout text-xs-center wrap>
<v-flex xs12>
<!-- 画像のプレビュー表示領域 -->
<v-img :src="upimage.fileUrl" aspect-ratio="2" :contain="true"></v-img>
<p>{{ upimage.fileName }}</p>
<p>圧縮前サイズ(MB):{{ fileInfo.before.size }}</p>
<p>圧縮後サイズ(MB):{{ fileInfo.after.size }}</p>
</v-flex>
<v-flex xs12>
<!-- ファイルの選択 -->
<input @change="selectedFile" type="file" accept="image/jpeg, image/jpg, image/png">
</v-flex>
<v-flex xs12>
<!-- Submitボタン -->
<v-btn color="primary" :disabled="isUploading">submit</v-btn>
</v-flex>
</v-layout>
</v-container>
</template>
<script>
export default {
data() {
return {
isUploading: false, // 画像ファイルアップロード中の判断フラグ
upimage: { fileUrl: "", fileName: "", blob: null } // 画像ファイル
};
},
methods: {
async selectedFile(e) {
// ファイル入力時の処理
// e.target.filesにファイルの情報が格納
},
async submit() {
// 画像をサーバに送信する処理
}
}
};
</script>
画面のリサイズ
入力された画像をプレビュー表示前にリサイズする処理を作成します。
まずは、npmかyarnでbrowser-image-compressionをインストールします。
npm install browser-image-compression --save
or
yarn add browser-image-compression
次に以下の2つの処理を作成します。
- 入力された画像ファイルの圧縮を行う処理
- プレビュー表示用にDataUrlを取得する処理
DataUrlにするのはimgタグにおいてプレビュー表示を行うためですので、直接アップロードしたい場合は不要です。
画像の圧縮時には最大のファイルサイズおよび、解像度を指定します。
今回は最大サイズは1MB, 解像度を800に指定しています。
その他にもオプションがありますので、詳細はbrowser-image-compressionのGitHubを参照してください。
import imageCompression from "browser-image-compression";
export default {
// アップロードされた画像ファイルを取得
async getCompressImageFileAsync(file) {
const options = {
maxSizeMB: 1, // 最大ファイルサイズ
maxWidthOrHeight: 800 // 最大画像幅もしくは高さ
};
try {
// 圧縮画像の生成
return await imageCompression(file, options);
} catch (error) {
console.error("getCompressImageFileAsync is error", error);
throw error;
}
},
// プレビュー表示用のdataurlを取得
async getDataUrlFromFile(file) {
try {
return await imageCompression.getDataUrlFromFile(file);
} catch (error) {
console.error("getDataUrlFromFile is error", error);
throw error;
}
}
};
画面への処理の取り込み
この画像処理を先ほどのレイアウト内で呼び出せば終了です。
// 略
<script>
import ImageUtil from "../lib/imageUtil";
export default {
// 略
methods: {
async selectedFile(e) {
this.isUploading = true;
const file = e.target.files[0];
if (!file) {
return;
}
try {
// 圧縮した画像を取得
const compFile = await ImageUtil.getCompressImageFileAsync(file);
//ファイルサイズの表示
this.fileInfo.before.size = (file.size / 1024 / 1024).toFixed(4);
this.fileInfo.after.size = (compFile.size / 1024 / 1024).toFixed(4);
// 画像情報の設定
this.upimage.blob = compFile;
this.upimage.fileUrl = await ImageUtil.getDataUrlFromFile(compFile);
this.upimage.fileName = file.name;
} catch (err) {
// エラーメッセージ等を表示
} finally {
this.isUploading = false;
}
},
submit() {
const fd = new FormData();
try {
fd.append(this.upimage.fileName, this.upimage.blob, this.upimage.fileName);
// ここにサーバーへのアップロード処理を実装する
} catch (err) {
// エラーメッセージ等を表示
}
}
}
};
</script>
まとめ
ということで、browser-image-compressionを使うことで、お手軽に画像の圧縮及び表示が可能となります。
是非試してみてください。