1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Cloud Functions for Firebaseで画像を保存/操作する

Posted at

Cloud Functions for Firebaseで画像を保存したい場合になかなかうまくいかず、QiitaやZenn上での情報も少なかったため、記事を残します。
下記コードはいずれもNode.js(JavaScript)で記載します。

はじめに

そもそもFirestore, Realtime Database, Cloud SQL等では画像を保存できないため、Cloud Storageに保存して、その場所を各DBに保存します。

取得した画像をCloud Storageに保存する

const admin = require("firebase-admin");

// jpgイメージの場合
const uploadImage = async (Id, image) => {
    const storage = admin.storage();

    const imageBuffer = Buffer.from(image, "binary");

    const fileName = "testfolder/testImage.jpg";
    const file = storage.bucket().file(fileName);

    // ストレージに画像を保存
    await file.save(imageBuffer, {metadata : {contentType : `image/jpeg`}});

    // firestoreに画像の場所を保存
    await admin.firestore().collection("Ids").doc(Id).update({
        "image" : fileName
    });
}

firebase-adminのstorage()を利用して、saveするだけです。

httpsリクエストから画像を受け取り、Cloud Storageに画像を保存する

firebase-functionsのhttps関数を使用して、multipart/form-dataで送信された画像を保存したい場合、busboyメソッドを使用することで保存することができます。

const functions = require("firebase-functions/v2");
const admin = require("firebase-admin");
const busboy = require("busboy");

exports.image = functions.https.onRequest(async (req, res) => {
    const storage = admin.storage();
    const bucket = storage.bucket();
    
    try {
        let fileName;
        const bb = busboy({ headers: req.headers });

        const busboyPromise = new Promise((resolve, reject) => {
            bb.on("file", (name, stream, info) => {
                fileName = info.filename;
                const distPath = bucket.file(fileName);

                // busboyのストリームを接続し、Cloud Storage上に保存
                stream.pipe(distPath.createWriteStream()).on("close", () => {
                    resolve();
                })
                .on("error", (error) => {
                    console.log("File processed error", error);
                    reject();
                });
            });
            bb.end(req.rawBody);
        });
    
        await busboyPromise;

        // ファイル名をFirestore上に保存
        const Id = req.query.Id;
        await admin.firestore().collection("Ids").doc(Id).update({
        "image" : fileName
        });
    
        res.status(200).send("image store success!");
    } catch (error) {
        res.status(400).send(error);
    }
});

当初はAPIでmultipart/form-data形式で送った画像をsave()メソッドで保存しようとしていたのですが、うまくいかず苦戦していました。。
一応busboyを使用することはGoogle Cloudのドキュメントにも書いています。なかなか気づけなかったってのが正直な感想ですが。

Cloud Storageの画像を取得する

fileNameからdownload()するだけです。

const admin = require("firebase-admin");

const getImage = async (fileName) => {
    const storage = admin.storage();
    const [image] = await storage.bucket().file(fileName).download();
    return image;
}

おまけ:mimeTypeを判定する

buffer型のデータからmimeTypeを判断する関数も載せておきます。
file-typeというライブラリがあるのですが、CommonJSでは使用できず、自作して使用していました。


exports.checkFileType = (byteArray) => {
    const patterns = {
        jpeg: [0xFF, 0xD8, 0xFF],
        png: [0x89, 0x50, 0x4E, 0x47],
        gif: [0x47, 0x49, 0x46],
        svg: [0x3C, 0x73, 0x76, 0x67], 
        // Add more patterns for other file types if needed
    };

    for (const [format, pattern] of Object.entries(patterns)) {
        let match = true;
        for (let i = 0; i < pattern.length; i++) {
            if (pattern[i] !== byteArray[i]) {
                match = false;
                break;
            }
        }
        if (match) {
            return format;
        }
    }

    return 'Unknown';
}

参考文献

  • Firebaseでmultipart/form-dataを処理する

  • busboy npm

  • Stream Node.js

  • file-type npm

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?