この記事でわかること
- typescript で express を構築し、ほぼ最小限で fierbase storage へのアップロード機能を実装する方法
- ソースコード(最後に記載
この記事でわからないこと
- firebase storage のルールを活用した例
- express や firebase の初期化
- firebase auth など firebase storage 以外の機能
- ローカルでアップロード機能を実現する方法
- firebase,typescript, express,multer などの細かい仕様
前提
- express と typescript の初期化が完了している
- firebase の初期化が完了している
- 筆者のわたしは express の実務経験はないため、express らしい書き方や誤っている部分などあればコメントください
実装
index.ts
ほぼ基礎的な index の書き方だと思っています。
HTTP ログが出なかったのが辛かったので morgan
というログ出力ミドルウェアをはさみました。
require("dotenv").config();
const express = require("express");
const app = express();
const morgan = require("morgan");
const PORT = process.env.PORT || 3000;
// middleware
app.use(express.json());
app.use(morgan("dev"));
app.use(express.urlencoded({ extended: true }));
// routes
app.use("/", require("./router.ts"));
app.listen(PORT);
views
upload.ejs
めちゃくちゃシンプルなファイルアップロードの HTML。
すがすがしい。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>test</title>
<style>
div {
margin-bottom: 20px;
}
</style>
</head>
<body>
<div>
<form action="/upload" method="post" enctype="multipart/form-data">
<div>
<input type="file" name="file" />
</div>
<div>
<input type="submit" value="送信する" />
</div>
</form>
</div>
</body>
</html>
.env
firebase の関数は環境変数で初期化するように実装したので、
.env ファイルをホームに用意して対応する firebase のシークレットを入れておきます。
PORT=3000
FIREBASE_API_KEY=xxx
FIREBASE_AUTH_DOMAIN=xxx
FIREBASE_PROJECT_ID=xxx
FIREBASE_STORAGE_BUCKET=xxx
FIREBASE_MESSAGING_SENDER_ID=xxx
FIREBASE_APP_ID=xxx
firebase.ts
初期化を行い、uploadFS という関数で firebase のアップロード関数を薄くラップします。
import { initializeApp } from "firebase/app";
import { getStorage, ref, uploadBytes, UploadResult } from "firebase/storage";
const firebaseConfig = {
apiKey: process.env.FIREBASE_API_KEY,
authDomain: process.env.FIREBASE_AUTH_DOMAIN,
projectId: process.env.FIREBASE_PROJECT_ID,
storageBucket: process.env.FIREBASE_STORAGE_BUCKET,
messagingSenderId: process.env.FIREBASE_MESSAGING_SENDER_ID,
appId: process.env.FIREBASE_APP_ID,
measurementId: process.env.FIREBASE_MEASUREMENT_ID,
};
initializeApp(firebaseConfig);
export const uploadFS = (
fileRef: string,
fileBuf: Blob | Uint8Array | ArrayBuffer
): Promise<UploadResult> => {
const storage = getStorage();
const storageRef = ref(storage, fileRef);
return uploadBytes(storageRef, fileBuf);
};
router.ts
import { Request, Response } from "express";
import multer from "multer";
import { uploadFS } from "./firebase";
const router = require("express").Router();
router.get("/", (req: Request, res: Response) => {
res.render("./upload.ejs");
});
router.post("/upload", multer().single("file"), (req: any, res: Response) => {
const fileBuf = req.file.buffer;
const fileRef = `${process.env.UPLOAD_BUCKET}/${req.file.originalname}`;
uploadFS(fileRef, fileBuf)
.then(() => {
res.redirect("/success");
})
.catch((err: any) => {
res.send(err);
});
});
router.get("/success", (req: Request, res: Response) => {
res.send("upload success");
});
module.exports = router;
multer を利用して File 形式で受け取るようにして、
先程作った関数に流し込みます。
このとき、バケットは環境変数で設定した UPLOAD_BUCKET
のバケットにアップロードされます。
また firebase storage でルールがロックモード担っている場合はデフォルトですべてのユーザーの read,weite 権限が拒否されているため、修正する必要があります。
参照
https://firebase.google.com/docs/rules/basics?authuser=0&hl=ja#default_rules_locked_mode
便宜上、以下の画像のようにすべてのユーザーの write,read が可能なガバガバ設定にしました。(当然、実際のプロダクトでは推奨されません。
実行
ts-node index.ts
- ページを開く
open http://localhost:3000/
2.ファイルを選択して送信
upload success
がでれば OK
- firebase storage でファイルを確認
トラブルシューティング
{"code":"storage/unauthorized","customData":{"serverResponse":""},"name":"FirebaseError","_baseMessage":"Firebase Storage: User does not have permission to access 'bucket-name/hoge.png'. (storage/unauthorized)"}
バケットの権限がないので、storage rule を修正する必要があります。詳細については routers.ts の章に記載
ソースコード