[JS] Firebaseの覚書 ⑧ Storageの操作まとめ

画像などのメディアファイルを保存できる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”.
Please update storage security rules to rules_version = “2” to use list.

Reference.list() Error Message

Storageのルールの先頭行にバージョンを明記すれば2系に変わる。

rules_version = '2';

list()listAll()の戻り値であるListResultにはitemsprefixes プロパティがあるが、それぞれ参照先が異なる。

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 が返す UploadTaskstate_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);
          });
        },
      );
    });
  }),
);

参考リンク

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください