画像などのメディアファイルを保存できるStorageについてのメモ。
先にFirestoreの操作を把握していると理解しやすいと思う。
※Firebaseの覚書は連番付けていますが内容は続いていません。
リファレンスとクラス図
Storageにはドキュメントという概念はなく、リファレンスが主体になっている。
ストレージの取得と参照の作成
V9
// V9: ストレージサービスへの参照を取得
import { getStorage } from 'firebase/storage';
const storage = getStorage();
// V9: ストレージの参照を作成
const storageRef = ref(storage);
// V9: imagesディレクトリの参照
const storageRef = ref(storage, 'images');
// V9: imagesディレクトリにある画像ファイルの参照
const storageRef = ref(storage, 'images/stars.jpg');
// V9: Google Cloud Storage URI から参照を作成する
const gsReference = ref(storage, 'gs://bucket/images/stars.jpg');
// V9: HTTPS URL から参照を作成する(URLはエスケープ処理が必要)
const httpsReference = ref(storage, 'https://firebasestorage.googleapis.com/b/bucket/o/images%20stars.jpg');
V8
// V8: ストレージサービスへの参照を取得
const storage = firebase.storage();
// V8: ストレージの参照を作成
const storageRef = storage.ref();
// V8: imagesディレクトリの参照
const imagesRef = storageRef.child('images');
// V8: imagesディレクトリにある画像ファイルの参照
const spaceRef = storageRef.child('images/stars.jpg');
// V8: Google Cloud Storage URI から参照を作成する
const gsReference = storage.refFromURL('gs://bucket/images/stars.jpg');
// V8: HTTPS URL から参照を作成する(URLはエスケープ処理が必要)
const httpsReference = storage.refFromURL('https://firebasestorage.googleapis.com/b/bucket/o/images%20stars.jpg');
ファイルリストの取得
list()
かlistAll()
を使用する。listAll
は全ファイルを取得するので、内容が小さいディレクトリ用。list()
はページ分割に対応できるので、内容が大きいディレクトリ用である。
V9
import { getStorage, ref, listAll } from "firebase/storage";
const storage = getStorage();
const listRef = ref(storage, 'images');
const imagesList = await listAll(listRef);
V8
const imagesList = await storageRef.child('images').listAll();
セキュリティールールのエラー
セキュリティルールがバージョン1だからlist(listAll)は使えねーよと怒られた場合、
Listing objects in a bucket is disallowed for rules_version = “1”.
Reference.list() Error Message
Please update storage security rules to rules_version = “2” to use list.
Storageのルールの先頭行にバージョンを明記すれば2系に変わる。
rules_version = '2';
list()
とlistAll()
の戻り値であるListResultにはitems
とprefixes
プロパティがあるが、それぞれ参照先が異なる。
Storageの構造が以下のようになっている場合、
images/icon/arrow.png
images/banner/1.png
images/main.png
items
にはimages直下にあるファイルの参照、prefixes
にはサブディレクトリの参照が配列で格納される。
imagesList {
items: [
Reference { name: 'main.png' }
],
prefixes: [
Reference { name: 'icon' },
Reference { name: 'banner'}
]
}
ファイルのダウンロードURLを取得する
ファイルのURLはgetDownloadURL()で得られる。
getDownloadURL()はPromiseを返すので、Promise.allでおk。
ファイル単体
// V9
import { getStorage, ref, listAll } from "firebase/storage";
const storage = getStorage();
const imageRef = ref(storage, 'images/stars.jpg');
getDownloadURL(imageRef)
.then((url) => {
// `url` is the download URL for 'images/stars.jpg'
})
.catch((error) => {
// Handle any errors
});
listAllの場合
V9
import { getStorage, ref, listAll } from "firebase/storage";
const storage = getStorage();
const listRef = ref(storage, 'images');
const imagesList = await listAll(listRef);
const urls = await Promise.all(
imagesList.items.map(ref => getDownloadURL(ref))
)
V8
const imagesList = await storageRef.child('images').listAll();
const urls = await Promise.all(
imagesList.items.map(ref => ref.getDownloadURL())
);
返されるURLはフルパスなので、そのまま表示に使うことができる。
[
"https://firebasestorage.googleapis.com/v0/b/…=media&token=…", "https://firebasestorage.googleapis.com/v0/b/…=media&token=…"
]
ファイルのアップロード
まず、保存するファイルの参照を ref で作成する。
// V9
import { getStorage, ref } from "firebase/storage";
const storage = getStorage();
const imageRef = ref(storage, 'images/star.jpg');
フォームから受け取ったFileまたはBlobの場合、uploadBytes に渡すだけで保存ができる。
const result = await uploadBytes(imageRef, file)
アップロードの管理
途中でキャンセルするような操作が必要な場合は、 uploadBytesResumable を利用する。
// V9
const uploadTask = uploadBytesResumable(imageRef, file);
// Pause the upload
uploadTask.pause();
// Resume the upload
uploadTask.resume();
// Cancel the upload
uploadTask.cancel();
アップロードの進行状況を監視する
uploadBytesResumable が返す UploadTask の state_changed
にイベントハンドラを設定しておくと、アップロードの進捗を表示することができる。
// V9
const uploadTask = uploadBytesResumable(imageRef, file);
uploadTask.on('state_changed',
(snapshot) => {
// 進行状況、一時停止、再開などの状態変化イベントを観察する
// アップロードされたバイト数とアップロードされる合計バイト数を含む、タスクの進行状況を取得します
const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
console.log('Upload is ' + progress + '% done');
switch (snapshot.state) {
case 'paused':
console.log('Upload is paused');
break;
case 'running':
console.log('Upload is running');
break;
}
},
(error) => {
// 失敗したアップロードの処理
},
() => {
// 成功したアップロードを完了時に処理する
// たとえば、ダウンロード URL を取得します: https://firebasestorage.googleapis.com/...
getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => {
console.log('File available at', downloadURL);
});
}
);
複数のファイルをアップロードする
やることは単体ファイルのアップロードと同じなので、Promise.allを使えば良い。
// V9
const fileList = [] // Files[]
const downloadURLs = await Promise.all(
fileList.map((file) => {
return new Promise<string>((resolve, reject) => {
const storageRef = ref(storage, `images/${file.name}`);
const uploadTask = uploadBytesResumable(storageRef, file);
uploadTask.on(
'state_changed',
(snapshot) => {
const progress =
Math.round((snapshot.bytesTransferred / snapshot.totalBytes) * 100);
console.log(`Upload is ${progress}% done`);
// ....
},
(error) => {
switch (error.code) {
case 'storage/unauthorized':
// User doesn't have permission to access the object
console.error(`${file.name} Upload Error: unauthorized`)
break;
case 'storage/canceled':
// User canceled the upload
break;
case 'storage/unknown':
// Unknown error occurred, inspect error.serverResponse
console.error(`${file.name} Upload Error: unknown`);
break;
}
reject();
},
() => {
getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => {
console.log(file.name, 'available at', downloadURL);
resolve(downloadURL);
});
},
);
});
}),
);