1

The following google cloud function properly uploads an image, but I would also like to compress the image as to avoid unnecessary charges due to large files being uploaded. Any suggestions would be greatly appreciated!!! the code is as follows:

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

  busboy.on("file", (fieldname, file, filename, encoding, mimetype) => {
    if (mimetype !== `image/jpeg` && mimetype !== `image/png`) {
      return res.status(400).json({ error: `Not an acceptable file type` })
    }

    // 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(config.storageBucket)
      .upload(imageToBeUploaded.filepath, {
        resumable: false,
        metadata: {
          metadata: {
            contentType: imageToBeUploaded.mimetype
          }
        }
      })
      .then(() => {
        const imageUrl = `https://firebasestorage.googleapis.com/v0/b/${config.storageBucket}/o/${imageFileName}?alt=media`
        return db.doc(`/users/${req.user.uid}`).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)
}
Doug Stevenson
  • 297,357
  • 32
  • 422
  • 441
Rob Terrell
  • 2,398
  • 1
  • 14
  • 36
  • 1
    Image files are already compressed. If you want more compression, resize the image to be smaller, or re-compress the image using a different algorithm. – Doug Stevenson Mar 03 '20 at 20:10
  • yes i understand they are already compressed but i would like to recompress so people arent uploading massive high pixelated images. how exactly can i write that logic – Rob Terrell Mar 03 '20 at 20:12
  • There are libraries out there that allow you to perform image manipulation programmatically. I suggest doing some research to find one and experiment with it. – Doug Stevenson Mar 03 '20 at 20:24

1 Answers1

5

You can set a maximum file size client-side and validate before submission. At the moment, anyone can enable an extension (on the Firebase Console Menu) to automatically resize any image uploaded to the project's firebase storage bucket. You just have to check the option to delete the original file after resizing is complete. More info here: https://firebase.google.com/products/extensions/storage-resize-images

nvictorme
  • 51
  • 3
  • BTW, to generate a really unique filename, you better use UUIDv1 or UUIDv4. Math.random() function is not strictly random and eventually you'll get the same filename causing it to replace previous files content. – nvictorme Mar 03 '20 at 21:30
  • For some reason when I implement the extension it resizes and deletes the original. Only issue is the profile pictures I’m storing on the user table are no longer being stored there is there something I need to do to ensure The resized image is stored in the user table – Rob Terrell Mar 03 '20 at 22:42
  • You can configure the extension to either delete the original file or not. I noticed you are storing the object signed-url as imageUrl in the users document. This is a very bad idea for multiple reasons. You should only store the filename OR the reference path to the object in storage, and then use the getDownloadUrl(ref) method client-side to retrieve the image asynchronously whenever it's needed. – nvictorme Mar 04 '20 at 21:09