1

The code is from a great tutorial "Full Stack React & Firebase Tutorial - Build a social media app". It allows the user to upload an image profile pic among other things... The issue I have is that it worked fine under Firebase 8 but since upgrading to Firebase 9 I cannot see how to figure out what is not working. I have added the const firebase = require('firebase/compat/app'); instead of the const firebase = require("firebase"); that it would normaly require before v9 and all my other functions either work or i have found the new tree shaken ways to make them work. The function uses Busboy and by the NPM page it looks like it should work but it is not. Here is the code if anyone has any ideas:

const { admin, db } = require("../util/admin");

const config = require("../util/config");
const { uuid } = require("uuidv4");

const firebase = require("firebase");
firebase.initializeApp(config);



// Upload a profile image for user
exports.uploadImage = (req, res) => {
  const BusBoy = require("busboy");
  const path = require("path");
  const os = require("os");
  const fs = require("fs");

  const busboy = new BusBoy({ headers: req.headers });

  let imageToBeUploaded = {};
  let imageFileName;
  // String for image token
  let generatedToken = uuid();

  busboy.on("file", (fieldname, file, filename, encoding, mimetype) => {
  console.log(fieldname, file, filename, encoding, mimetype);
  if (mimetype !== "image/jpeg" && mimetype !== "image/png") {
    return res.status(400).json({ error: "Wrong file type submitted" });
  }
  // my.image.png => ['my', 'image', 'png']
  const imageExtension = filename.split(".")[filename.split(".").length - 1];
  // 32756238461724837.png
  imageFileName = `${Math.round(
    Math.random() * 1000000000000
  ).toString()}.${imageExtension}`;
  const filepath = path.join(os.tmpdir(), imageFileName);
  imageToBeUploaded = { filepath, mimetype };
  file.pipe(fs.createWriteStream(filepath));
  });
  busboy.on("finish", () => {
   admin
   .storage()
   .bucket()
   .upload(imageToBeUploaded.filepath, {
     resumable: false,
     metadata: {
       metadata: {
         contentType: imageToBeUploaded.mimetype,
         //Generate token to be appended to imageUrl
         firebaseStorageDownloadTokens: generatedToken,
       },
     },
   })
   .then(() => {
    // Append token to url
    const imageUrl = 
`https://firebasestorage.googleapis.com/v0/b/${config.storageBucket}/o/${imageFileName}? 
   alt=media&token=${generatedToken}`;
     return db.doc(`/users/${req.user.handle}`).update({ imageUrl });
   })
   .then(() => {
     return res.json({ message: "image uploaded successfully" });
   })
   .catch((err) => {
     console.error(err);
      return res.status(500).json({ error: "something went wrong" });
   });
 });
  busboy.end(req.rawBody);
};
RJC
  • 1,224
  • 2
  • 12
JGC
  • 73
  • 6
  • Can you also include what error/s you've encountered when you run your code? Additionally, include your `admin` and `config` file (note: omit any confidential informations) to allow the community provide a solution or find the root cause of your error. – RJC Jun 16 '22 at 03:28
  • So many different errors. Just kept changing things trying to get something going. It's what i like to call "Brute force Coding". In the end i was able to get it up and running with a piece of code from the Google Cloud Functions sample code for Node.js. https://github.com/GoogleCloudPlatform/nodejs-docs-samples/blob/HEAD/functions/http/index.js I think that the mimetype was one of the biggest issues. I also feel that there is some un-needed extra code in the async await but its kind of like -> don't fix what is not broken! Perhaps i will revisit when i have more time. – JGC Jun 21 '22 at 22:17

1 Answers1

1

Here is the fix for others with the same issues.

// UploadImage for profile pic   ****Newtoken
exports.uploadImage = (req, res) =>{
if (req.method !== 'POST') {
    // Return a "method not allowed" error
    return res.status(405).end();
  }
  const newToken = req.user.newToken; //From fbAuth
  const busboy = Busboy({headers: req.headers});
  const tmpdir = os.tmpdir();
//Lets
let imageToBeUploaded = {};
let imageFileName,uuid;
//bucket
const bucket = admin.storage().bucket(config.storageBucket);
  // This object will accumulate all the fields, keyed by their name
  //const fields = {};

  // This object will accumulate all the uploaded files, keyed by their 
  name.
  const uploads = {};
  const fileWrites = [];

  // This code will process each file uploaded.
  busboy.on('file', (fieldname, file, {filename}) => {

      // File type check
    const imageExtension = filename.split(".")[filename.split(".").length - 
    1];
    if(imageExtension !== 'jpeg' && imageExtension !== 'jpg' && 
   imageExtension !== 'png') {
        return res.status(400).json({error: 'Wrong file type submitted'});
    }
    //Creating a download storage token
    uuid = `${Math.round( 
    Math.random() * 1000000000000
    ).toString()}`;
    
    imageFileName = `ImageProfilePic.${imageExtension}`;

    const filepath = path.join(tmpdir, filename);
    uploads[fieldname] = filepath;
   
    imageToBeUploaded = filepath ;
   
    const writeStream = fs.createWriteStream(filepath);
    file.pipe(writeStream);
    //Add to Bucket
    bucket.upload(imageToBeUploaded, {
         //destination:'Users/user1/file.jpg',
         destination:`Users/${req.user.handle}/${imageFileName}`,
         resumable: false,
         metadata: {
             metadata: {
                 firebaseStorageDownloadTokens: uuid,
                 contentType: `image / ${filename}`
             },
         },
     })
     .then(() => { //Add uploaded image to "imageUrl" in user with update 
     method
                                 //this needs to look at the 
 Users/handleName storage area for image
        const imageUrl = 

 `https://firebasestorage.googleapis.com/v0/b/${config.storageBucket} 
  /o/Users%2F ${req.user.handle}%2F${imageFileName}?alt=media&token 
  =${uuid}`;
         return db.doc(`/Users/${req.user.handle}`).update({ imageUrl });
    })
    .catch((err) => {
        console.error(err);
        return res.status(500).json({ error: "something went wrong"});
     });
    const promise = new Promise((resolve, reject) => {
      file.on('end', () => {
        writeStream.end();
      });
      writeStream.on('finish', resolve);
      writeStream.on('error', reject);
    });
    fileWrites.push(promise);
  });
  busboy.on('finish', async () => {
    await Promise.all(fileWrites);
  
    /**
     * TODO(developer): Process saved files here
     */
   
    for (const file in uploads) {
        console.log("File",file);
      fs.unlinkSync(uploads[file]);
    }
    var Respond = {
        message: 'Image Uploaded Successfully',
        NewToken: newToken
    };
     return res.json(Respond);
   // res.send(Respond);
  });

  busboy.end(req.rawBody);

};

Think there should be a try / catch in here also to handle errors better:) Thanks to all who took the time. Cheers

JGC
  • 73
  • 6