0
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?

More than 1 year has passed since last update.

express * typescript でfirebase storage へのアップロード機能をサクッと実装する

Posted at

この記事でわかること

  • 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 というログ出力ミドルウェアをはさみました。

index.ts
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。
すがすがしい。

image.png

upload.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 のシークレットを入れておきます。

.env
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 のアップロード関数を薄くラップします。

 firebase.ts
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

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 が可能なガバガバ設定にしました。(当然、実際のプロダクトでは推奨されません。

image.png

実行

ts-node index.ts
  1. ページを開く
open http://localhost:3000/

image.png

2.ファイルを選択して送信

image.png

upload success がでれば OK

  1. firebase storage でファイルを確認

68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3236323537382f66623432363634342d616462642d633030342d313137322d6632396361323764656135342e706e67.png

トラブルシューティング

{"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 の章に記載

ソースコード

0
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
0
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?