AWS | Firebase

[ Firebase ] 이미지 업로드에 관하여 ( Feat. Multer, Busboy )

shiro21 2023. 3. 23. 18:28
반응형

NodeJS에서 Firebase로 이미지를 업로드하기 위해 이것저것 사용해 봤던 방법입니다.

:: 결론은 실패하고 NextJS에서 직접 Firebase로 이미지를 올려줬습니다. local에서는 제대로 올라가는데, 실서버에 올리고서 도전하면 업로드가 되지 않아 4일간 멘붕상태에 빠져있었습니다..

( 로컬에서는 모두 정상적으로 작동하고, 실제 서버에서는 작동하지 않습니다. 전부 undefined로 들어오게 됩니다... )

 

FrontEnd에서는 Axios를 사용했으며, app.ts와 firebase.ts는 생성했다는 가정하에 실행합니다.

 

FrontEnd에서 넘겨준 내용 ( 간략하게 )

const uploads = async () => {
    const formData = new FormData();

    if (!file) return;
    for (let i = 0; i < file.length; i++) formData.append("multipartFiles", file[i]);
    
    await api.post("/test/fileAdd", formData)
    .then(edit => {
      console.log("양호");
      console.log(edit);
      setImage(edit.data.data);
    })
    .catch(err => console.log("why", err))
}

 

1. Multer

가장 유명하고 내용이 많은 방법입니다.

 

npm

<!-- Multer -->
$ npm install multer@1.4.3
$ npm install @types/multer@1.4.3

<!-- Firebase -->
$ npm install multer-firebase-storage

 

service를 만들어서 multer를 밖에서 적용시켜 줬습니다.

uploadService.ts

import "dotenv/config";

const FirebaseStorage = require('multer-firebase-storage');
import multer from "multer";

export const editMulter = multer({
    storage: FirebaseStorage({
        bucketName: process.env.FIRE_BUCKET,
        credentials: {
            clientEmail: process.env.FIRE_CEMAIL,
            privateKey: (process.env.FIRE_PRIVATE_KEY !== undefined ? process.env.FIRE_PRIVATE_KEY.replace(/\\n/g, '\n') : ""),
            // privateKey: process.env.FIRE_PRIVATE_KEY,
            projectId: process.env.FIRE_PROJECT_ID
        },
        directoryPath:`edit`,
        unique: true
    }),
})

multer와 함께 multer-firebase-storage를 사용하면 더 편하게 파일을 생성할 수 있습니다.

 

serviceController.ts

import express, { Request, Response, NextFunction } from "express";
import { editMulter } from "../service/uploadService";

// 하나의 파일을 받을떄는 .single, 여러개의 파일을 받을 경우는 .array입니다.
router.post("/fileAdd", editMulter.array("multipartFiles"), async (req: Request, res: Response) => {

    const files: Express.Multer.File[] = req.files as Express.Multer.File[];
    // // const files: any | Express.Multer.File[] = req.files;

    console.log(files);

    try {
        const storage = getStorage();

        const url = await getDownloadURL(ref(storage, files[0].path));

        console.log(url);
        res.status(200).json({
            code: "y",
            data: url
        });
        // await getDownloadURL(ref(storage, files[0].path))
        // .then((url: any) => {
        //     res.status(200).json({
        //         code: "y",
        //         data: url
        //     })
        // })
        // .catch((err: any) => {
        //     console.log("DownLoad Err", err)
        // });
    }
    catch(err) {
        console.log(err);
    }

});

아래 주석된 부분은 풀어쓴 내용입니다.

 


 

2. Multer

첫번째 방법에서 문제가 multer-firebase-storage라고 생각해서 이 내용을 제거하고 실행했었습니다.

 

serviceController.ts

import express, { Request, Response } from "express";
import multer from "multer";
import { ref, getStorage, uploadBytes } from "firebase/storage";
const { v4: uuidv4 } = require("uuid");

const upload = multer({ storage: multer.memoryStorage()});

router.post("/fileAdd", upload.array('multipartFiles'), async (req: Request, res: Response, next) => {

    // const storage = getStorage();

    /* files폴더 내부에 originalname으로 이름을 정해줬습니다. */
    // const storageRef = ref(storage, `files/${req.file.originalname}`);

    /* req.file.buffer자리에는 Blob or File을 넣어줄 수 있습니다. */
    // await uploadBytes(storageRef, req.file.buffer)
    // .then((snapshot) => {
    //     console.log("file uplaoded", snapshot);
    // });
    
    let uid = uuidv4();
    
    const storage = getStorage();

    const storageRef = ref(storage);

    const imageRef = ref(storageRef, "cover");

    const spaceRef = ref(imageRef, uid + "-" + req.file.originalname);

    await uploadBytes(spaceRef, req.file.fileRef)
    .then(snapshot => {
        console.log(snapshot);
    })
    .catch(err => console.log("UPLOAD ERR", err));

});

위 주석된 부분은 줄여 쓴 내용입니다.

file이름을 originalname으로만 해주면 중복될 가능성이 있기 때문에, uuid를 사용해서 고유이름으로 만들었습니다.

 


 

3. Busboy

Firebase와 Multer는 맞지 않는다면서, 모두가 추천을 엄청나게 해주던 모듈입니다.. ( 그런데 정보는 진짜 없습니다.. 추천만 해줍니다..하.. )

 

npm

busboy-firebase는 어떻게 사용하라는건지 잘 모르겠습니다..

<!-- busboy -->
$ npm install busboy
$ npm install @types/busboy
$ npm install busboy-firebase

 

uploadService.ts

const Busboy = require('busboy');
const { v4: uuidv4 } = require("uuid");

export const filesUpload = async (req: Request, res: Response, next: NextFunction) => {

    let busboy = Busboy({ headers: req.headers })
    let uid = uuidv4();

    await busboy.on('multipartFiles', async (fieldname: string, file: any, filename: any, encoding: any, mimetype: any) => {
        console.log("file 들어옴");
        const storage = getStorage();

        const storageRef = ref(storage)

        const imageRef = ref(storageRef, "cover");

        const spaceRef = ref(imageRef, uid + "-" + filename.filename);

        await file.on("data", async (data: any) => {
            console.log("Data 들어옴");
            await uploadBytes(spaceRef, data)
            .then(async (snapshot) => {
                console.log("uploadBytes 들어옴");
                await getDownloadURL(ref(storage, snapshot.metadata.fullPath))
                .then(_url => {
                    console.log("getDownloadURL 들어옴");
                    res.status(200).json({
                        code: "y",
                        data: _url
                    })
                })
            })
        })

        busboy.on('error', (e: any) => {
            console.log("ERR", e);
        });
    
    })

    busboy.on('finish', (e: any) => {
        next();
    })

    req.pipe(busboy);
}

 

serviceController.ts

router.post("/fileAdd", filesUpload, async (req: any, res: Response, next) => {
    res.status(200)
});

res.status(200)신호를 안주면 중간에 동작에 멈춰서 넣었습니다.

 

 


 

4. 구글 클라우드 변경하기

아! 오류내용이 CORS오류라고 뜨거나 상태코드 500이 뜨거나 했었기에 찾아보니 CORS문제일 수도 있다면서 구글 클라우드 내부에서 편집을 해줘야 한다는 내용도 많이 있었기에 그 내용도 적용시켜 봤습니다.

( Firebase Storage는 주소가 또 다르기 때문이라는 내용을 보고 적용시켜봤던 내용입니다. )

 

구글 클라우드 바로가기

 

상단에 터미널을 연다음 편집기 열기를 클릭합니다.

 

VS Code와 생김새가 같기 때문에 작성할때 어려움을 없을거라 생각됩니다.

먼저 cors.json 파일을 생성해준다음 아래와 같은 내용을 입력해줍니다.

 

cors.json

origin 배열안에 *을 입력하면 모든 도메인에 대해 허용하고, 도메인을 입력해주면 그 도메인들만 허용해줍니다.

[
    {
      "origin": ["*"],
      "method": ["GET"],
      "maxAgeSeconds": 3600
    }
]

 

다시 터미널로 돌아와서 아래처럼 입력해줍니다.

exampleproject.appspot.com부분은 스토리지부분 이름입니다.

gsutil cors set cors.json gs://exampleproject.appspot.com

 

아래부분입니다. gs:// ~~~

 

터미널에 내용을 입력하고 엔터를 누르고 정상적으로 실행되었을 아래같은 내용이 나오고 승인을 누르면 적용됩니다.

 

 

이외에도 formidable, Multiparty등을 사용해봤는데 뭔가 맞지 않아 포기하고 결국 마지막 방법으로 NextJS내부에서 직접 생성해주고 마무리를 하였습니다.

 

덕분에 Firebase deploy 하루 무료 제한수가 5번이라는 것도 알았네요 ㅋㅋ

4일간 꽤 많은 deploy를 했는지.. 무려 비용이 ₩81이나 나왔습니다.

에뮬레이터로 확인해볼걸 그랬습니다..ㅎㅎ

Firebase 배포 관련 내용 바로가기

반응형