Edited at

ブラウザ側でサイズ圧縮(リサイズ)して画像表示やアップロードを行う(Vue.js)


概要

Webアプリでクライアントサイド(ブラウザ)で画像を圧縮(リサイズ)する方法の紹介です。

サンプルとして下図のような入力した画像をプレビュー表示した上で、確定時にサーバーに画像をアップロードするまでのサンプルコードを紹介します。

※今回はvue.jsとvuetifyによるサンプルになりますが、UIのレイアウト部分以外は基本的に一般的なjavascirptですので

他のフレームワークを使用している場合等でも参考にはなるかと思います

sample.png

動くデモとGitHubのサンプルコードは下記です。

Demo

GitHub


背景

Webアプリで、画像ファイルをアップロードするといったシチュエーションはよくあると思います。

スマホ等で撮影した写真等だと、解像度やファイルサイズも大きいため、そのまま使用せず一度リサイズしたりする事が多いかと思います。

アップロード後にサーバ側で処理するといった手法もありますが、今回はWEBブラウザ側でリサイズする方法とします。


実現方法

当初はCanvasにリサイズした画像を描画して、再度データ化する方法を使用していました。

参考

しかしながら、もっとお手軽にかつファイルサイズも小さくしてアップできる、Browser Image Compression といったものがあるのでそちらを使用します。


画面レイアウト

以下のような、3つの領域を作成します。


  • 画像のプレビュー領域

     入力した画像のプレビュー表示します。サンプルではvuetifyのv-imgタグを使用していますが、imgタグに相当するもの配置します。合わせてファイル名も表示します。


  • 画像の入力

     ファイルを入力するinputタグです。ファイル選択対アログで画像を選択するとプレビュー領域に表示されます。


  • Submitボタン

     プレビュー表示した画像をサーバにアップロードするためのボタン。画像入力中(リサイズ中)はプレビュー表示されるまでは押せなくします。


これらをvue.jsのコンポーネントで書くと下記のようになります。

処理等は無く、まだレイアウトだけです。


ImageUpload.vue

<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つの処理を作成します。


  1. 入力された画像ファイルの圧縮を行う処理

  2. プレビュー表示用にDataUrlを取得する処理

DataUrlにするのはimgタグにおいてプレビュー表示を行うためですので、直接アップロードしたい場合は不要です。

画像の圧縮時には最大のファイルサイズおよび、解像度を指定します。

今回は最大サイズは1MB, 解像度を800に指定しています。

その他にもオプションがありますので、詳細はbrowser-image-compressionのGitHubを参照してください。


ImageUtil.js

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;
}
}
};



画面への処理の取り込み

この画像処理を先ほどのレイアウト内で呼び出せば終了です。


ImageUpload.vue

// 略

<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を使うことで、お手軽に画像の圧縮及び表示が可能となります。

是非試してみてください。