browser-image-compressionを使って、簡単に画像のリサイズをすることができます。
あまりの手軽さに感動したのでまとめます。
browser-image-compression
の導入と利用については既に素晴らしい記事がありますので、ぜひご参考ください。
当記事でもbrowser-image-compression
の使い方はさらっと紹介しつつ、Firebase Storageへのアップロードまで一気通貫でまとめます。
なお、以下がプロジェクトに導入、設定済みであることを前提としています
- Vuetify
- Firebase
#browser-image-compressionの導入
npmもしくはyarnで導入します。
yarn add browser-image-compression
#リサイズ・アップロード
###(準備)リサイズ用モジュールの作成
import imageCompression from 'browser-image-compression'
export default {
async getCompressImageFile(file) {
const options = {
maxSizeMB: 0.8, //最大ファイルサイズ
maxWidthOrHeight: 600, //最大縦横値
}
return await imageCompression(file, options)
},
}
リサイズしたい目的によって、単にファイルサイズを小さくしたいこともあれば、画像のサイズを一定以下にしないといけないこともあると思います。
今回私が利用したケースにおいては画像サイズが重要だったため、maxWidthOrHeightを指定しています。maxSizeMBは念の為に指定していましたが、なくても良かったかもです。
もっと言うとこれぐらいの記述量なら外出ししなくてもよかったかも……
###(本題)アップロード処理
先ほどのリサイズ処理をアップロード前に呼んでリサイズを行い、リサイズ後画像をアップロードします。
流れを大きく分けると以下の3段階です。
- フォーム上で選択された画像をscriptで受け取る
- 受け取った画像をリサイズする
- リサイズ後の画像をFirebase Storageにアップロードする
先にコード全体を掲載し、そのうえで順に触れていきます。
<v-file-input
accept="image/*"
label="写真"
prepend-icon="mdi-camera"
@change="uploadImage"
></v-file-input>
import { storage } from '@/plugins/firebase'
import imageCompression from '@/imageCompression.js'
//中略
async uploadImage(fileInfo) {
//選択された画像の情報を取得
this.imageInfo = fileInfo
//画像をリサイズする
console.log('start compress')
this.compressedImage = await imageCompression.getCompressImageFile(
this.imageInfo
)
console.log('finished compress')
//storageへの参照
const storageRef = storage.ref()
const xxxImagesRef = storageRef.child('xxx/' + uniqueImageName)
//アップロードしてURLを取得
//コールバック関数内からthisへ参照できないため、selfに退避する
let self = this
xxxImagesRef.put(this.compressedImage).then(async function (snapshot) {
self.imageUrl = await snapshot.ref.getDownloadURL()
})
},
import Vue from 'vue'
import { firestorePlugin } from 'vuefire'
import firebase from 'firebase/app'
import 'firebase/firestore'
import 'firebase/storage'
Vue.use(firestorePlugin)
const firebaseApp = firebase.initializeApp({
//xxx部分は各々の内容で置換
apiKey: 'xxxxxxxxxxxxxxxxxxxxxxxxxxx',
authDomain: 'xxxxxxxxxxxxxxxxxxxxxxxxxxx',
databaseURL: 'xxxxxxxxxxxxxxxxxxxxxxxxxxx',
projectId: 'xxxxxxxxxxxxxxxxxxxxxxxxxxx',
storageBucket: 'xxxxxxxxxxxxxxxxxxxxxxxxxxx',
messagingSenderId: 'xxxxxxxxxxxxxxxxxxxxxxxxxxx',
appId: 'xxxxxxxxxxxxxxxxxxxxxxxxxxx',
measurementId: 'xxxxxxxxxxxxxxxxxxxxxxxxxxx',
})
export const db = firebaseApp.firestore()
export const storage = firebaseApp.storage()
※私はfirebase.jsを切り出していますが、main.jsに同内容を記述している方はよしなに読み替えてください。
###1.フォーム上で選択された画像をscriptで受け取る
まずは画像アップロードフォームを作ります。Vuetifyでさくさく作ります。
<v-file-input
accept="image/*"
label="写真"
prepend-icon="mdi-camera"
@change="uploadImage"
></v-file-input>
v-file-input
は、特に複雑なことをしなくても、このような記述だけで任意のメソッドにファイルの情報を渡すことができます。今回の例であれば、ファイルが選択された(あるいは変更された)ときにそのファイルをuploadImage
に渡し、リサイズとアップロードを行います。
※@change
だとファイルを変更するたびにアップロードが発生するので、Storage上にゴミが溜まります。ユースケース次第で、@change
ではリサイズのみに留めてもよいでしょう。
続いて、v-file-input
で選択された画像ファイルを受け取るための記述は以下のみです。引数としてファイルを受け取ります。
async uploadImage(fileInfo) {
//選択された画像の情報を取得
this.imageInfo = fileInfo
ここの概念がまだ理解できていません。単にファイルの属性情報などだけを受け取るのではなく、まさにファイルとして振る舞うモノを変数として受け取っているということに違和感があります……
###2.受け取った画像をリサイズする
//画像をリサイズする
this.compressedImage = await imageCompression.getCompressImageFile(
this.imageInfo
)
script上部でimportしたimageCompression
の中のgetCompressImageFile
を動かします。
1.で受け取った画像ファイル(this.imageInfo
)を引数にし、戻り値(=リサイズ後の画像)を新しい変数に格納しています。
###3.リサイズ後の画像をFirebase Storageにアップロードする
最後は2.で得られたリサイズ後の画像をアップロードします。
//storageへの参照
const storageRef = storage.ref()
const xxxImagesRef = storageRef.child('xxx/' + uniqueImageName)
//アップロードしてURLを取得
//コールバック関数内からthisへ参照できないため、selfに退避する
let self = this
xxxImagesRef.put(this.compressedImage).then(async function (snapshot) {
self.imageUrl = await snapshot.ref.getDownloadURL()
})
まずはともあれStorageへの参照を作ります。Storage内部の任意の子ディレクトリに保存したい場合、既存の参照からさらにchild()
メソッドでもう一段階深い参照を作ることができます。(xxxの部分は任意のディレクトリ名に読み替えてください)
適当な参照が作れたら、その参照のput()
メソッドでファイルをアップロードします。
単にアップロードするだけでなく、アップロードしたファイルのダウンロード用URLも欲しかったので、コールバック関数内でgetDownloadURL()
によってURLを取得しています。
一連の処理が完了すると、Storageは以下のようになっています。
もとの画像が17MBほどだったので、かなりの容量を節約することができました!
#参考