Firebase part VI : Uploading an image

2nd Oct 19
blossom

I will be adding the functionality to add images.

I am using the node package busboy in facilitating the image upload.  

npm install --save busboy

Before I create a function for that route I need to make some changes to postOneBonsai function.  I need to add a slug to the bonsai rather than firebase auto generate an id.  So that firebase returns silver birch doc when I visit '/bonsai/silver-birch'.  For more information on how to add data to database with cloud Firestore click here.

exports.postOneBonsai = (req, res) => {
    const newBonsai = {
        body: req.body.body,
        family: req.body.family,
        name: req.body.name,
        slug: slugify(req.body.name)
        
    };
   
    db.doc(`/bonsais/${newBonsai.slug}`)
        .get()
        .then(doc => {
             if(doc.exists){
                 return res.status(400).json({name: 'this tree has is already been added'});
             }else{
                 newBonsai.bonsaiId + doc.id;
                return db.doc(`/bonsais/${newBonsai.slug}`).set(newBonsai)
             }
        })
        .then(()=>{
            res.json({message: `document ${newBonsai.bonsaiId} created successfully`})
        })
  
        .catch(err => {
            console.error(err); 
            res.status(500).json({error: 'something when wrong'})
        })

I add a slug to the newBonsai object.  I create a function called slugify that converts the bonsai name to an url readable name. In the then block where it returns the document I assign an id to the bonsai called bonsaiId.

 const slugify = (string) => {
    return string
    .toString()
    .trim()
    .toLowerCase()
    .replace(/\s+/g, '-') 
  }

 

Now I create a route for uploading an image for a specific bonsai: 

 app.post('/bonsai/:bonsaiSlug/image', FBAuth, uploadImage);

 

Then I use busboy on file upload to create the file/image:

  exports.uploadImage = (req, res) => {
    const Busboy = require('busboy');
    const path = require('path');
    const os = require('os');
    const fs = require('fs');
    let imageFileName;
    let imageUpload = {};

    const busboy = new Busboy({headers: req.headers});
    busboy.on('file', (fieldname, file, filename, encoding, mimetype) => {
        const imageExt = filename.split('.')[filename.split('.').length - 1];
        imageFileName = `${Math.round(Math.random()*100000000)}.${imageExt}`;
        const filepath = path.join(os.tmpdir(),imageFileName);
        imageUpload = {filepath, mimetype};
        file.pipe(fs.createWriteStream(filepath));

    });

Once the image has been created, the 'finish' listener will be triggered.  Within the callback is where I will upload the file to firebase storage. Please look at the Firebase admin SDK documentation for more info. In the then block we need to add the image url to the bonsai document.  We retrieve the bonsai document using the slug parameter passed in the route and run the method update to add the image url to the document.

    busboy.on('finish', () => {
        admin.storage().bucket().upload(imageUpload.filepath, {
            resumable: false,
            metadata: {
                metadata: {
                    contentType: imageUpload.mimetype
                }
            }
        })
        .then(() => {
        const imageUrl = `https://firebasestorage.googleapis.com/v0/b/${config.storageBucket}/o/${imageFileName}?alt=media`;
        return db.doc(`/screams/${req.params.bonsaiSlug}`).update({ imageUrl });
       
        })
        .then(() => {
            res.json({message: 'Image uploaded successfully'})
        })
        .catch(err => {
            console.error(err);
            res.status(500).json({error:err.code})
        });
    });

 busboy.end(req.rawBody);
}

 

As we are not using the firebase client library but checking authentication through functions, we need to make some changes to the storage rules to just allow read.

rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read
    }
  }
}

 

Now in Postman I test my route by uploading an image with a logged in user and success I upload the image to storage and save the url path in my bonsai document.

Though I am not checking for the file type yet: 

 

I have recently completed my apprenticeship.  I am currently building the API to a React JS site using Firebase.