Firebase Cloud function to parse form data working incorrectly

Viewed 77

I have an app which calls a cloud function endpoint:

import './App.css';
import React from 'react';
import axios from 'axios';

function App() {

  const [file, setFile] = React.useState(null);
  function fileSelected(e)
  {
    setFile(()=> e.target.files[0]);
  }
  function uploadFile()
  {
    console.log(file)
    const fd = new FormData();
    fd.append('image', file, file.name);
    console.log(file);
    console.log(file.name);
    axios.post('https://us-central1-athelasapp.cloudfunctions.net/uploadFile', fd)
    .then(res => {
      console.log(res)
    });
  }
  return (
    <div className="App">
      <input type="file" onChange={fileSelected}/>
      <input type="submit" onClick={uploadFile}/>
    </div>
  );
}

export default App;

and the endpoint tries to parse it with Busboy however, it throws a 500 error. I have Busboy imported but it throws:

xhr.js:220 POST https://us-central1-athelasapp.cloudfunctions.net/uploadFile 500`

const functions = require("firebase-functions");
const express = require("express");
const cors = require("cors");
const app = express();
const Busboy = require("busboy");
const os = require("os");
const path = require("path");
const fs = require("fs");

const gcconfig = {
  projectId: "athelasapp",
  keyFilename: "athelasapp-firebase-adminsdk-yojnp-1e9141a009.json",
};

const {Storage} = require("@google-cloud/storage");
const gcs = new Storage(gcconfig);
app.use(cors({origin: "http://localhost:3000"}));
// // Create and Deploy Your First Cloud Functions
// https://firebase.google.com/docs/functions/write-firebase-functions


exports.uploadFile = functions.https.onRequest(app);

app.post("/", (req, res) =>{
  if (req.method !== "POST") {
    return res.status(500).json({
      message: "Method Does Not Work",
    });
  }

  const busboy = new Busboy({headers: req.headers});
  let uploadData = null;

  busboy.on("file", (fieldname, file, filename, encoding, mimetype) => {
    const filepath = path.join(os.tmpdir(), filename);
    uploadData = {file: filepath, type: mimetype};
    file.pipe(fs.createWriteStream(filepath));
    res.status(200).json({
      imageDetails: uploadData,
    });
  });

  busboy.on("finish", ()=>{
    const bucket = gcs.bucket("athelasapp.appspot.com");
    bucket.upload(uploadData.file, {
      uploadType: "media",
      metadata: {
        metadata: {
          contentType: uploadData.type,
        },
      },
    });
  }).then(() => {
    res.status(200).json({
      message: "Method Works!",
    });
  }).catch((err) =>{
    res.status(500).json({
      message: "Method Failed!",
    });
  });
  busboy.end(req.rawBody);
  res.status(200).json({
    message: "Method Works",
  });
});

I cant find any errors in my code or how it's implemented? Could it be I'm passing in the wrong things in the request? I think it might have to do with the nomenclature of Busboy

1 Answers

Here's a working gist streaming directly instead of creating the temporary file: https://gist.github.com/PatrickHeneise/8f2c72c16c4e68e829e58ade64aba553#file-gcp-function-storage-file-stream-js

function asyncBusboy(req, res) {
  return new Promise((resolve, reject) => {
    const storage = new Storage()
    const bucket = storage.bucket(process.env.BUCKET)

    const fields = []
    const busboy = Busboy({
      headers: req.headers,
      limits: {
        fileSize: 10 * 1024 * 1024
      }
    })

    busboy.on('field', (key, value) => {
      fields[key] = value
    })

    busboy.on('file', (name, file, fileInfo) => {
      const { mimeType } = fileInfo
      const destFile = bucket.file(fileName)
      const writeStream = destFile.createWriteStream({
        metadata: {
          contentType: fileInfo.mimeType,
          metadata: {
            originalFileName: fileInfo.filename
          }
        }
      })
      file.pipe(writeStream)
    })

    busboy.on('close', function () {
      return resolve({ fields })
    })

    if (req.rawBody) {
      busboy.end(req.rawBody)
    } else {
      req.pipe(busboy)
    }
  })
}
Related